Waarom het leren van C nog steeds belangrijk is voor moderne ontwikkelaars

16

Je hoeft C niet elke dag te gebruiken om het te respecteren. De taal is alomtegenwoordig. Het drijft de kernel van uw besturingssysteem aan, stuurt ingebedde apparaten aan en dient als basislaag voor giganten als Python, Java en C++. Als u codeur bent, schrijft u waarschijnlijk meestal in talen van een hoger niveau. Dat is prima. Maar weten hoe je C moet leren, biedt een voorsprong die weinig andere vaardigheden kunnen evenaren. Het gaat niet alleen om het schrijven van efficiënte code; het gaat over het begrijpen van de machine.

Wanneer je C begrijpt, behandel je je computer niet meer als een magische doos. Je begint geheugenbeheer, afvalinzameling en hardwarebeperkingen als tastbare concepten te zien. Dankzij deze zichtbaarheid kunt u betere code schrijven in elke taal. Het is het verschil tussen weten hoe je een auto moet besturen en weten hoe je de motor moet repareren.

Voor IT-professionals zijn de voordelen nog directer. Systeembeheer omvat vaak het schrijven van scripts en het onderhouden van complexe omgevingen. De meeste shells (de gecontroleerde uitvoeringsomgevingen waarin scripts worden uitgevoerd) zijn gebaseerd op C. De C shell (of csh) blijft een populaire aanpassing voor IT-professionals die besturingssystemen op een laag niveau moeten manipuleren. Als u de onderliggende structuur van deze tools begrijpt, gaat het oplossen van problemen sneller en is er minder giswerk.

De oorsprong van C en UNIX

Om te begrijpen waarom C is gestructureerd zoals het is, moet je naar de geboorte ervan kijken. Het begin van de jaren zeventig was een chaotische tijd voor programmeren. Als je met hardware wilde praten, gebruikte je assembleertaal. Het was uitgebreid. Debuggen was een nachtmerrie. Het toevoegen van functies vereiste vervelend, handmatig werk dat de ontwikkeling tot stilstand bracht.

Ken Thompson en Dennis Ritchie van Bell Labs hadden een betere manier nodig. Ze werkten aan het UNIX-besturingssysteem. Hun eerste poging tot een taal op hoog niveau heette B, gebouwd bovenop de programmeertaal van het BCPL-systeem. Toen Bell Labs een PDP-11 van Digital Equipment Corporation kreeg, paste Thompson B aan om aan de eisen van de nieuwe hardware te voldoen. Het resultaat wasC.

Het was niet alleen een nieuwe syntaxis. Het was een verandering in de filosofie. In 1973 was C zo stabiel dat de hele UNIX-kernel erin werd herschreven. Dit was een gedurfde zet. Het bewees dat talen op hoog niveau taken op laag niveau konden verwerken zonder dat dit ten koste ging van de prestaties.

Standaardisatie en het “Witboek”

In het begin was C een beetje een wild west. Verschillende programmeurs creëerden hun eigen dialecten. Om dit op te lossen, hebben ontwikkelaars in de jaren tachtig standaarden ontwikkeld. De eerste grote mijlpaal was “The C Programming Language” van Brian Kernighan en Dennis Ritchie. C-fans noemen het K&R, of het ‘Witboek’.

De originele versie van C, afgeleid van het K&R-boek, wordt vandaag de dag nog steeds K&R C genoemd.

Het boek werd de definitieve gids. De tweede editie, gepubliceerd in 1988, blijft voor velen een referentiepunt. Maar boeken zijn geen standaarden. Het American National Standards Institute (ANSI) heeft in 1989 standaard X3.159-1989 uitgebracht. De International Organization for Standardization (ISO) volgde in 1990 met ISO/IEC 9899:1990.

Deze normen gaven C zijn moderne identiteit. Mogelijk ziet u verwijzingen naar C89, C90 of C99 in de documentatie. Dit is slechts een afkorting voor de standaard die in die jaren werd uitgebracht. Als iemand ‘ANSI C’ zegt, heeft hij het meestal over de standaard uit 1989. Door deze standaardisatie kon C zich buiten Bell Labs verspreiden. Het werd een universeel hulpmiddel, geen propriëtair hulpmiddel.

De erfenis: C++ en Java

Het succes van C zorgde voor een rimpeleffect. Computers werden sneller. Toepassingen werden complexer. Programmeurs hadden manieren nodig om deze complexiteit te beheersen zonder het wiel opnieuw uit te vinden.

Voer C++ in. Er was C voor nodig en er werden objectgeoriënteerde programmeerfuncties (OOP) aan toegevoegd. Hierdoor konden ontwikkelaars code effectiever hergebruiken. Het optimaliseerde de mogelijkheid om grootschalige softwareprojecten met veel bewegende delen uit te voeren. Java volgde een soortgelijk pad, waarbij het zwaar leende van de syntaxis van C en tegelijkertijd het geheugenbeheer vereenvoudigde.

Deze talen hebben C niet vervangen. Ze vertrouwden erop. C bleef de brug tussen de applicatiecode en de hardware. Het is de onzichtbare basis. Zonder C zouden de talen op hoog niveau die u dagelijks gebruikt geen consistente manier hebben om met de machine te praten.

Waarom C nog steeds relevant is

Het argument tegen C is dat het oud is. Het is handmatig. Het is gevaarlijk. Als u een aanwijzer verprutst, kunt u uw systeem in één regel code laten crashen. Maar dat gevaar is ook zijn kracht. C geeft u totale controle. Het verbergt geen dingen voor je.

Voor ontwikkelaars betekent dit dieper inzicht. Als je weet hoe het geheugen in C wordt toegewezen, begrijp je waarom je Python-script traag werkt. Je ziet de overhead. Je ziet de afvalinzameling in realtime. Met deze kennis kunt u optimaliseren. Hiermee kunt u programma’s schrijven die niet alleen werken, maar ook efficiënt werken.

Voor IT-specialisten gaat het om controle. Scripttalen zijn handig. Ze schrijven snel. Maar als er iets diep in de stapel kapot gaat, moet je begrijpen wat er onder de motorkap gebeurt. C biedt die context. Het verklaart waarom een ​​schaal zich op een bepaalde manier gedraagt. Het legt uit hoe een besturingssysteem bronnen beheert.

C leren gaat niet over het worden van een C-programmeur. Het gaat erom een ​​betere programmeur te worden. Het gaat om het zien van de draden. En als je de draden eenmaal ziet, kun je ze nooit meer echt losmaken.

C wordt niet rechtstreeks uitgevoerd. Je kunt niet zomaar op een bronbestand dubbelklikken en het over het scherm zien dansen. Het is een gecompileerde taal. Dat betekent dat je voor mensen leesbare tekst eerst moet vertalen naar machinaal uitvoerbare instructies. Bij dit proces is een compiler betrokken. De compiler scant uw code, controleert op syntaxisfouten en als hij tevreden is met wat hij ziet, spuugt hij een uitvoerbaar bestand uit. Dat is het binaire bestand dat de computer daadwerkelijk uitvoert.

U kunt C-code in vrijwel elke teksteditor schrijven. Windows-gebruikers gebruiken mogelijk standaard Kladblok. Mac-gebruikers grijpen vaak naar TextEdit. Linux-mensen geven misschien de voorkeur aan gedit. De tool doet er niet zoveel toe als de output. U maakt een tekstbestand zonder opmaak. De magie gebeurt in de volgende stap.

Uw compiler zoeken en instellen

Voordat u ook maar één regel complexe logica schrijft, heeft u de juiste hulpmiddelen nodig. Als je macOS of een Linux-distributie zoals Ubuntu gebruikt, heb je al wat je nodig hebt. U hoeft alleen maar de ontwikkelhulpmiddelen voor uw besturingssysteem te installeren. Dit geeft u toegang tot een C-compiler.

Dit zijn opdrachtregelprogramma’s. U zult geen glimmende knop vinden waarop u kunt klikken in uw toepassingsmenu. Je opent een terminalvenster. Van daaruit typt u opdrachten. Het standaardcommando is meestal cc of gcc. Na de opdracht typt u opties en argumenten. Dit zijn de instructies die de compiler vertellen wat hij moet doen.

Of als u GCC gebruikt:

Het is eenvoudig. Het is ook meedogenloos als je een fout maakt.

IDE’s voor Windows en platformonafhankelijke behoeften

Commandoregels zijn niet voor iedereen. Als u de voorkeur geeft aan een grafische interface, of als u Windows gebruikt, wilt u misschien een Integrated Development Environment (IDE). Een IDE bundelt alles. Je schrijft code. Jij compileert het. Jij debugt het. Je doet het allemaal in één venster. Fouten worden gemarkeerd. Oplossingen zijn gemakkelijker te vinden.

Voor Windows is Microsoft Visual C++ een sterke optie. Het kan zowel C als C++ verwerken. Het is krachtig. Het is ook duur als je de gratis Community-editie niet gebruikt.

Eclipse is een andere populaire keuze. Het is gratis. Het is op Java gebaseerd, maar ondersteunt ook C en vele andere talen. Het draait op Windows, Mac en Linux. Als u al deel uitmaakt van het Eclipse-ecosysteem, is het toevoegen van de C/C++-plug-in een logische stap.

Waarom compilerversies belangrijk zijn

De versie van de compiler die u gebruikt, is geen klein detail. Het is van cruciaal belang. U moet een compilerversie gebruiken die gelijk is aan of nieuwer is dan de C-taalstandaard waarvan uw code afhankelijk is. Als u moderne C99- of C11-code probeert te compileren met een oude compiler, zal dit mislukken. Het zal de nieuwe syntaxis niet begrijpen.

Als u een IDE gebruikt, controleer dan uw projectinstellingen. Zorg ervoor dat de IDE is geconfigureerd voor de specifieke C-versie die u gebruikt. Als u zich in de terminal bevindt, geeft u deze informatie door via opdrachtregelargumenten.

Een GCC-opdracht opsplitsen

Laten we naar een echt voorbeeld kijken. Met deze opdracht bestuurt u expliciet het compilatieproces.

Deze regel vertelt de compiler precies wat hij moet doen. Splits het op:

  • gcc : Dit roept de GNU Compiler Collection aan. Het is de motor.
  • -std=c99 : Dit zet de standaard. Het vertelt gcc om de C99-versie van de C-taal te gebruiken. Zonder dit zou het systeem mogelijk standaard een oudere, restrictievere standaard moeten hanteren.
  • -o mijnprogramma.exe : Dit stelt de naam van het uitvoerbestand in. Zonder de -o vlag zal gcc uw uitvoerbare bestand standaard a.out noemen. Dat is niet erg beschrijvend.
  • mijnprogramma.c : Dit is uw bronbestand. De invoer.

Het commando luidt feitelijk: “Neem myprogram.c, compileer het met behulp van C99-regels en sla het resultaat op als myprogram.exe.”

Navigeren door opties

Er zijn honderden opties beschikbaar voor gcc en andere compilers. Je hoeft ze niet te onthouden. Je hoeft ze niet allemaal te gebruiken. Maar je moet wel weten dat ze bestaan.

Blader door de documentatie voor uw specifieke compiler. Zoek naar vlaggen die de prestaties optimaliseren. Zoek naar vlaggen die het waarschuwingsniveau verhogen. Elke optie die u toevoegt, verandert hoe het uiteindelijke binaire bestand zich gedraagt.

Je eerste C-programma schrijven

Nu de compiler is geïnstalleerd en geconfigureerd, bent u klaar om te coderen. De toetredingsdrempel is laag. De concepten zijn fundamenteel.

Begin met de basisstructuur. Elk C-programma heeft een hoofdfunctie nodig. Er zijn headers nodig. Er is een retourverklaring nodig. Dit zijn de botten.

Dit is het eenvoudigste geldige C-programma. Het bevat een standaard invoer/uitvoerbibliotheek. Het definieert een hoofdfunctie. Er wordt een string afgedrukt. Het retourneert nul om succes aan te geven.

Sla dit bestand op als hello.c. Compileer het.

Voer het uit.

U zou “Hallo wereld” op uw scherm moeten zien. Je hebt zojuist geschreven, gecompileerd en

De anatomie van een Hello World-programma

Begin met een leeg tekstbestand. Noem het sample.c. Noem het niet ‘sample.txt’. Als uw editor standaard ‘.txt’ gebruikt, zal de compiler dit afwijzen. Je hebt de extensie .c nodig om aan te geven dat dit de C-broncode is en geen tekstdocument.

Hier is de code. Het is kaal. Minimaal.

Compileer het. Voer het uit. De computer drukt een string af en sluit af. Eenvoudig genoeg. Maar kijk eens dichterbij. Elk personage hier heeft een taak.

Regel één is een opmerking. De tags /* en */ vertellen de compiler dat hij alles daarin moet negeren. Mensen lezen deze. De machine niet. Het is voor jou, of voor degene die deze code later onderhoudt.

Regel twee is waar dingen echt worden. #include haalt een bibliotheek op. Met name de standaard invoer/uitvoerbibliotheek. Dit bestand bevat definities voor functies zoals printf. U schrijft de logica voor het afdrukken niet op het scherm. U vertelt de compiler alleen waar hij de instructies kan vinden die al bestaan.

Lijn drie definieert het toegangspunt. int hoofd(). Elk C-programma heeft dit nodig. Het is het eerste wat het besturingssysteem oproept. Het trefwoord int betekent dat de functie een geheel getal retourneert. De haakjes zijn hier leeg, maar dat is een detail voor later.

Regels vier en zeven zijn accolades. { en }. Ze omhullen de hoofdtekst van de functie. Sommige ontwikkelaars plaatsen de openingsaccolade op dezelfde regel als main(). Anderen, zoals dit voorbeeld, zetten het op een eigen regel. Het verandert niets aan de manier waarop de code wordt uitgevoerd. Het verandert alleen hoe het eruit ziet. En in C is de leesbaarheid van belang, omdat je hier lang naar zult staren. Inspringen helpt. Gebruik spaties.

Regel vijf roept printf aan. Deze functie bevindt zich in de stdio.h -bibliotheek die u eerder hebt toegevoegd. Er is een stringargument voor nodig. Let op de \n aan het einde. Dat is een ontsnappingssequentie. Er wordt geen backslash en een n afgedrukt. Het verplaatst de cursor naar de volgende regel. Zonder dit zou uw opdrachtprompt direct na uw uitvoer onhandig zitten. En kijk naar de puntkomma. ;. Elke uitspraak moet eindigen met één. Als je het mist, verslikt de compiler zich.

Lijn zes is het uitgangssignaal. retourneer 0;. Wanneer main klaar is, geeft het een waarde terug aan het besturingssysteem. Nul betekent succes. Niet-nul betekent meestal dat er iets kapot is. Mogelijk gebruikt u deze waarde niet in uw code, maar het systeem doet dat wel. Wanneer u scripts test of programma’s in batchtaken uitvoert, controleert het besturingssysteem die nul. Het is het groene licht dat het proces is voltooid zonder te crashen.

Compileren vanaf de opdrachtregel

Nu je de code hebt, moet je deze omzetten in iets dat de CPU begrijpt. Je hebt een compiler nodig. Laten we aannemen dat u gcc gebruikt.

Open uw terminal. Navigeer naar de map waarin u sample.c heeft opgeslagen. Typ dit:

Verdeel de opdracht. gcc is het compileerprogramma. -o staat voor uitvoer. U vertelt hoe het resulterende bestand moet worden genoemd. sample.exe is die naam. sample.c is het invoerbestand.

Als u een puntkomma verkeerd heeft getypt, mislukt deze regel. De uitvoer is een muur met rode tekst. Het is een syntaxisfout. Je hebt iets verkeerd getypt. Misschien ben je een citaat vergeten. Misschien heb je een beugel gemist. Corrigeer de code. Redden. Voer de opdracht opnieuw uit. Het is een cyclus.

Als het werkt, verschijnt er een nieuw bestand. voorbeeld.exe. Dit is het uitvoerbare bestand. Het is niet langer een voor mensen leesbare tekst. Het is machinecode.

Het uitvoerbare bestand uitvoeren

Staar niet alleen naar het nieuwe bestand. Voer het uit.

De ./ is belangrijk. Het vertelt de shell dat hij in de huidige map moet zoeken naar het uitvoerbare bestand. Als u dit weglaat, kan het systeem in uw pad kijken en het niet vinden, of erger nog, het verkeerde pad vinden.

Wanneer het wordt uitgevoerd, ziet u de tekst. Vervolgens beweegt de cursor. Dan bent u terug bij uw opdrachtprompt.

Dat is de levenscyclus. Schrijven. Compileren. Loop. Pauze. Repareren. Herhalen.

Het is geen magie. Het zijn gewoon heel strikte regels. Je volgt ze en de computer doet precies wat je zegt. Je negeert ze, en het doet niets.

Maar dit is slechts het oppervlak. Je hebt een functie gezien. Je hebt een bibliotheek gezien. U heeft een retourwaarde gezien. De bouwstenen zijn er. De echte complexiteit ontstaat wanneer je ze met elkaar gaat verbinden.

Als je C-programmeerconcepten probeert te begrijpen, ben je niet de enige. Het is een taal die precisie vereist. Je schrijft niet alleen code; je beheert het geheugen, definieert typen en vertelt de CPU precies wat hij moet doen. Geen magie hier. Gewoon logica.

Hier volgt een overzicht van wat er feitelijk gebeurt als u een C-programma compileert.

Hoe functies werken in C

Laten we beginnen met functies in C. In veel moderne talen worden dit methoden genoemd. In C zijn het functies. Een functie is een codeblok. Het doet één ding. Wanneer uw programma draait, voert het deze blokken uit.

U kunt verschillende functies definiëren. U kunt ze vanuit andere functies bellen. Hiermee kunt u een enorm programma in beheersbare, herbruikbare secties opdelen.

De basisstructuur ziet er als volgt uit:

Elk C-programma heeft minimaal één functie nodig: main. De compiler zoekt naar deze specifieke naam om de uitvoering te starten. Zelfs als main andere functies aanroept, is dit het toegangspunt.

Beschouw dit eenvoudige voorbeeld:

Het heeft een integer-retourtype. Geen parameters. Twee uitspraken. Het printf commando stuurt tekst naar het scherm. De return 0 vertelt het systeem dat het programma succesvol is beëindigd.

Parameters doorgeven aan functies

Andere functies hebben definities en oproepen nodig. Een functieaanroep is een statement binnen een andere functie. Het benoemt de functie. Het bevat haakjes. Als de functie gegevens verwacht, moet u deze opgeven. Dit worden doorlaatparameters genoemd.

Wat is een parameter? Het zijn gegevens van een specifiek type. De functie heeft het nodig om te werken. C-functies kunnen een onbeperkt aantal parameters accepteren. Dit worden ook wel argumenten genoemd.

Elke parameter vereist een gegevenstype en een variabelenaam. Meerdere parameters worden gescheiden door komma’s.

Kijk naar deze functie:

Er zijn twee gehele getallen nodig. Het verdubbelt ze. Het voegt ze toe. Het resultaat wordt geretourneerd. Eenvoudig. Effectief.

Variabelen en gegevenstypen

Variabelen zijn tijdelijke aanduidingen. Ze staan ​​voor waarden die je nog niet kent. Je hebt flexibiliteit nodig. U wilt getallen niet hardcoderen in uw logica. C behandelt dit als algebra.

Maar C is strenger. U moet gegevenstypen definiëren. Dit vertelt de compiler hoeveel geheugen hij moet toewijzen. Het vertelt de CPU hoe de bits moeten worden geïnterpreteerd.

Elk gegevenstype heeft een grootte. Het wordt gemeten in binaire bits of bytes. Het heeft regels. Als u het verkeerde type kiest, kan uw programma crashen of slechte resultaten opleveren. Het kiezen van de juiste datatypen in C is van cruciaal belang voor prestaties en correctheid.

Operaties en controlestroom

Je voert rekenkundige bewerkingen uit op getallen. Je voegt strings samen. C heeft ingebouwde bewerkingen voor deze taken.

Maar de controlestroom is waar de logica leeft.

Loops herhalen acties. Ze herhalen zich op basis van omstandigheden. C biedt verschillende lusstructuren:
terwijl
doen/terwijl
voor
doorgaan /pauze
ga naar (gebruik met voorzichtigheid)

Het heeft ook voorwaardelijke bepalingen:
als/dan/anders
schakelaar/kast

Met deze structuren kunt u het pad van het programma bepalen. Doe iets. Controleer een voorwaarde. Herhalen. Breek indien nodig.

Gegevensstructuren en geheugen

Als je veel data hebt, heb je datastructuren in C nodig. Je moet het sorteren. Zoek het.

Een datastructuur is een gestructureerde manier om meerdere gegevensstukken van hetzelfde type weer te geven. De meest voorkomende is een array. Het is een geïndexeerde lijst van een bepaalde grootte.

C heeft bibliotheken voor algemene structuren. Maar je kunt ook zelf schrijven. U kunt uw eigen functies definiëren. Je hebt volledige controle.

Aan deze controle zijn kosten verbonden. C vereist dat je met pointers omgaat. Het vereist dat u het geheugen beheert. Er is geen garbage collection in C. Als u geheugen toewijst, moet u dit vrijmaken. Als je dit vergeet, heb je een geheugenlek.

Preprocessorbewerkingen

Voordat de compiler uw code zelfs maar ziet, wordt de preprocessor uitgevoerd. Het geeft je instructies.

Het vervangt constante waarden. Het bevat code uit bibliotheken. Je hebt dit gezien met #include. Het haalt standaarddefinities binnen.

Dit maakt geen deel uit van het uiteindelijke uitvoerbare bestand. Het is een instelfase. Het bereidt uw code voor op compilatie.

Waarom dit belangrijk is

Dit overzicht kan compact aanvoelen. Als programmeren nieuw voor je is, is het veel om in je op te nemen. Maar dit zijn de bouwstenen.

Je kunt C niet schrijven zonder functies in C te begrijpen. U kunt geen gegevens beheren zonder gegevenstypen. Zonder lussen kun je de stroom niet controleren.

De taal houdt je hand niet vast. Het geeft je hulpmiddelen. Jij bepaalt hoe je ze gebruikt.

Vervolgens gaan we dieper in op hoe deze stukken in een groter programma passen. De structuur is belangrijk. De syntaxis is belangrijk. Maar de logica is het belangrijkst.

Hoe pak je het aan om een ​​nieuwe taal te leren? Begin je met variabelen? Of spring je meteen in de functies? Het pad is aan jou om te definiëren.

U kunt een functiedefinitie overal in een C-programma neerzetten, zolang deze maar niet in een andere functie is genest. Maar er is één strikte regel. U moet de compiler vertellen dat de functie bestaat voordat deze deze probeert te gebruiken. Hier komt het functieprototype om de hoek kijken. Het is een statement dat bovenaan uw code wordt geplaatst. Het weerspiegelt de eerste regel van uw feitelijke functiedefinitie. In C hoeft u de parameters in de bovenste regel geen naam te geven. U hebt alleen de gegevenstypen nodig. Hier is hoe doubleAndAdd eruit ziet als prototype:

int doubleAndAdd(int, int);

Beschouw functionele prototypes als de paklijst voor een nieuw meubelstuk. Je zou niet beginnen met het bouwen van een boekenplank zonder de doos op alle schroeven en planken te controleren. De compiler doet hetzelfde. Het controleert het prototype voordat uw programma wordt samengesteld. Het zorgt ervoor dat alle stukjes aanwezig zijn.

Als u met sample.c werkt, voeg dan het prototype, de definitie en de aanroep van doubleAndAdd toe. Compileer het. Voer het uit. Kijk hoe het werkt. De code ziet er als volgt uit:

We hebben de basisstructuur behandeld. Nu moeten we het over gegevens hebben. Welke soorten gegevens kun je verwerken in C? Welke bewerkingen zijn erop toegestaan?

Functiedeclaraties versus prototypes

Oudere C-programmeurs noemen ze functiedeclaraties. Je zult deze term veel horen. We gebruiken hier een functieprototype omdat het onderscheid ertoe doet. Voor een traditionele aangifte waren geen parameters nodig. Alleen een retourtype, een naam en lege haakjes waren voldoende. Een prototype is anders. Het geeft de compiler specifieke details. Het vertelt de compiler hoeveel parameters er zijn. Het vertelt welke gegevenstypen deze parameters zijn. Deze extra informatie voorkomt fouten. Het is de moderne beste praktijk. Het is van toepassing op C en vele andere talen. Zonder dit is de compiler aan het gissen. Hiermee weet de compiler precies wat hij kan verwachten.

Gegevenstypen zijn de bouwstenen. Operaties zijn de hulpmiddelen. We gaan van structuur naar substantie.

Computers beschouwen gegevens als onbewerkt binair getal. Het is gewoon een stroom van enen en nullen. Je harde schijf, geheugen en processor geven niets om de betekenis achter die bits. Alleen de software die op de machine draait, geeft die miljarden cijfers context.

C valt hier op. Het is een van de weinige talen op hoog niveau waarmee je gegevens op bitniveau kunt manipuleren en deze tegelijkertijd kunt interpreteren op basis van specifieke regels.

Hoe C gegevenstypen definieert

Een gegevenstype is in wezen een regelboek. Het vertelt de compiler hoe een reeks bits moet worden geïnterpreteerd. Het definieert de grootte van de gegevens en hoe bewerkingen zoals optellen of vermenigvuldigen erop moeten werken.

De grootte van een datatype in C is afhankelijk van de processor. Dit is niet opgelost.

Overweeg het standaardtype int. In een 16-bits processor is een geheel getal 16 bits lang. Schakel over naar een 32-bits of 64-bits processor, en diezelfde ‘int’ wordt 32 bits. Deze variatie is een kernonderdeel van het begrijpen van gegevenstypen in C.

Ondertekend versus niet-ondertekend: het bereikprobleem

Hoe C omgaat met positieve en negatieve getallen is een ander cruciaal detail.

Een ondertekend type reserveert één bit om het teken aan te geven. Hierdoor wordt uw bereik gehalveerd. Op een 16-bits systeem:

  • Een unsigned int varieert van 0 tot 65.535.
  • Een ‘ondertekende int’ varieert van -32.768 tot 32.767.

Als uw berekening een variabele buiten deze grenzen duwt, houdt C u niet altijd tegen. Het kan een overstroming veroorzaken. Je moet extra code schrijven om dat handmatig af te handelen. Het negeren hiervan leidt tot bugs.

Primitieve typen en arrays

C-programmeurs selecteren gegevenstypen op basis van programmabehoeften. De taal biedt primitieve gegevenstypen. Deze zijn ingebouwd. Zij vormen de basis.

Voor een volledige lijst van deze typen en conversieregels heeft u een naslaggids nodig. Maar er is één structuur die de kloof overbrugt tussen primitieven en complexe organisaties: de array.

Een array is een virtuele lijst. Alle elementen moeten van hetzelfde gegevenstype zijn. U kunt de grootte van een array niet meer wijzigen nadat deze is gemaakt. U kan de inhoud ervan naar een grotere of kleinere array kopiëren.

Tekenreeksen en headerbestanden

Hoewel getalarrays gebruikelijk zijn, hebben karakterarrays unieke kenmerken. Wij noemen ze snaren.

Een string slaat tekst op. Hiermee kan uw programma gebruikersinvoer of afdrukuitvoer opslaan. Omdat stringmanipulatie een dergelijke specifieke reeks bewerkingen met zich meebrengt, bevat C een speciaal headerbestand: string.h. Het bevat de typische functies die u nodig heeft om met tekst te werken.

Voorrang van operators is belangrijk

C bevat ingebouwde bewerkingen die in de meeste talen voorkomen. Maar de volgorde van de handelingen is niet altijd intuïtief als je een andere achtergrond hebt.

U moet de prioriteit van de operator kennen. Dit is de volgorde waarin de compiler wiskundige uitdrukkingen evalueert.

Neem dit voorbeeld:
(2+5)*3 is gelijk aan 21.
2+5*3 is gelijk aan 17.

C voert vermenigvuldiging uit vóór optelling. Haakjes overschrijven dit standaardgedrag. Een verkeerd begrip van de prioriteit leidt tot onjuiste logica.

Praktische stappen voor leerlingen

Als je C leert, ga dan niet alleen maar over de oppervlakte.

  1. Maak uzelf vertrouwd met alle primitieve gegevenstypen.
  2. Onthoud de prioriteit van de bewerkingen.
  3. Experimenteer met gemengde operaties.

Probeer gehele getallen en drijvers te mengen. Kijk hoe C de conversie afhandelt. Het is de enige manier om de regels te internaliseren.

Dit behandelt de basisprincipes van hoe C omgaat met gegevens en bewerkingen. Maar elke keer opnieuw programma’s schrijven is inefficiënt. De volgende logische stap is herbruikbaarheid.

Je hoeft niet voor elk C-programma het wiel opnieuw uit te vinden. Je moet niet helemaal opnieuw beginnen.

C is kaal. Het brengt alles terug tot de essentie. Het wordt niet geleverd met ingebouwde toetsenbordinvoer- of schermuitvoerfuncties. Als u naar de console wilt afdrukken, staat u er alleen voor, tenzij u een bibliotheek gebruikt.

Bibliotheken lossen dit op. Ze bevatten herbruikbare codeblokken. Gebruik ze.

Standaard versus aangepaste bibliotheken in C

We hebben de standaard I/O-bibliotheek al gezien. Het heet ‘stdio’. De compiler laadt het via de #include richtlijn. Het haalt gegevens uit het headerbestand stdio.h.

C-onderhouders bundelen standaardbibliotheken voor I/O, wiskunde, tijdmanipulatie en stringbewerkingen. U vindt de standaardbibliotheekgegevens van de C89 in elke goede referentie. C99 heeft er nog meer toegevoegd. Zoek naar die updates als je moderne functies nodig hebt.

Maar je kunt ook zelf schrijven.

Het heeft voordelen om uw code op te splitsen in herbruikbare modules. Kortere bestanden zijn gemakkelijker te lezen. Debuggen wordt minder pijnlijk. Het testen is meer geïsoleerd.

Om een ​​bibliotheek te koppelen, voegt u een #include regel toe.

Gebruik voor standaardbibliotheken hoekbeugels. Dit vertelt de compiler om in systeemmappen te kijken.

Gebruik dubbele aanhalingstekens voor uw eigen aangepaste bibliotheken. De compiler controleert eerst de lokale map.

Let op de syntaxis. Er is geen puntkomma nodig aan het einde van een #include -instructie. Het is geen functieaanroep. Het is een preprocessorrichtlijn.

Het schrijven van een bibliotheek is geen magie. De functiedefinities zijn identiek aan die in uw hoofdprogramma. Het verschil zit in het bouwproces.

U compileert uw bibliotheekcode in een objectbestand. Het eindigt op .o. U maakt een headerbestand. Het eindigt op .h. De header bevat functieprototypes. Het verklaart wat de functies doen zonder ze te implementeren.

Wanneer u uw hoofdprogramma bouwt, verwijst u naar het headerbestand in de #include regel. U geeft het objectbestand door aan de compileropdracht. De linker verbindt ze.

Deze modulaire aanpak houdt uw code schoon. Het scheidt interface van implementatie.

Aanwijzingen: de kern van C-geheugenbeheer

We hebben de basis besproken. Nu gaan we naar het geheugen.

Wanneer een C-programma draait, bevindt het zich in het RAM-geheugen. Elk stukje gegevens heeft een specifiek adres. Variabelen zijn niet alleen maar containers. Het zijn locaties.

Wanneer u een functie aanroept, wordt de code in het geheugen geladen. Het loopt. Het keert terug.

Standaard geeft C parameters door op waarde. Het kopieert de gegevens. De functie werkt op de kopie. De oorspronkelijke variabele in het hoofdprogramma blijft onaangeroerd.

Soms is dit wat je wilt.

Andere keren moet u de oorspronkelijke gegevens wijzigen. U moet de variabele op de oorspronkelijke geheugenlocatie wijzigen.

Om dit te doen, geeft u een aanwijzer door. Je geeft het adres door. Dit is ‘door referentie heen’.

Aanwijzingen zijn overal in C aanwezig. Zonder deze kun je C niet effectief gebruiken.

Een pointer is een variabele. Maar er wordt een adres opgeslagen. Het verwijst naar een ander stuk gegevens. Er is ook een soort. Het type vertelt de computer hoeveel bytes er op dat adres moeten worden gelezen.

Het kan lastig zijn om aanwijzingen in code te vinden. Zelfs experts missen ze.

Wanneer u een aanwijzer declareert, is dit duidelijk. Je gebruikt een sterretje. Dit is de indirectie-operator.

Hier is ‘i’ een geheel getal. p is een verwijzing naar een geheel getal.

Geen van beide heeft nog een waarde.

Wijs ‘i’ een nummer toe.

Laat nu p naar i wijzen. U gebruikt het adres-van-operator. Het is een ampersand (& ).

De & betekent ‘adres van’. U hoeft het werkelijke geheugenadres niet te weten. Het verandert elke keer dat u het programma uitvoert. De compiler zoekt het uit.

Als u de & overslaat, wijst u de waarde 3 toe aan de aanwijzer. Dat is een bug. De aanwijzer bevat nu het nummer 3, niet de locatie van i.

Aanwijzers correct gebruiken in C

Het gebruik van pointers introduceert complexiteit. U beheert het geheugen rechtstreeks.

De compiler zal u niet behoeden voor fouten.

Als u een null-pointer derefereert, crasht uw programma. Als u toegang krijgt tot geheugen dat u niet bezit, krijgt u een segmentatiefout. Het is lelijk.

Maar de kracht is reëel. U kunt grote datastructuren doorgeven zonder ze te kopiëren. U kunt variabelen over functiegrenzen heen wijzigen. U kunt gekoppelde lijsten, bomen en complexe gegevensstructuren bouwen.

Het beheersen van pointers is het verschil tussen het schrijven van eenvoudige scripts en het bouwen van robuuste systemen. Het is niet optioneel. Het is de basis.

Geef een aanwijzer door, niet de waarde zelf.

U kunt elke keer dat ze een gegevenstype delen, een pointer inwisselen voor de bijbehorende variabele. Dit is van belang bij functieaanroepen en wiskunde. Neem int b. Je kunt b = *p + 2; schrijven. Het sterretje vertelt de compiler dat hij de verwijzing naar p moet verwijderen. Het pakt de werkelijke waarde op het geheugenadres. Niet het adres zelf. Dit onderscheid is alles.

Zonder verwijzingen is het vrijwel onmogelijk om code op te splitsen in functies buiten ‘main’. Je zou vastzitten met spaghetticode of globale variabelen. Beschouw een variabele ‘h’ voor de hoogte in centimeters. Je wilt dat een functie setHeight de gebruiker om invoer vraagt.

Dit ziet er redelijk uit. Dat is het niet. De functie ontvangt een kopie van h. Het verandert de kopie. De originele h blijft onaangeroerd. Wanneer de functie wordt afgesloten, verdwijnt die kopie. Uw lengte blijft niet-geïnitialiseerd of nul.

Om h daadwerkelijk bij te werken, heeft de functie een referentie nodig. Concreet: een aanwijzer. Wijzig de parameterlijst om een ​​adres te accepteren.

Je hebt nu twee manieren om dit aan te roepen.

Gebruik eerst de adres-van operator & direct op h.

setHeight(&h);

Ten tweede: maak een tussenliggende aanwijzer.

Beide werken. Ze verwijzen naar dezelfde plek in het geheugen. Dit onthult een kernuitdaging van aanwijzingen. Meerdere pointers kunnen naar dezelfde waarde verwijzen. Verander er één, en ze zien allemaal de verandering.

Is dit goed of slecht? Hangt af van je bedoeling. Het is krachtig voor het bijwerken van de gedeelde status. Het is gevaarlijk als u niet meer weet welke aanwijzer de gegevens bestuurt. Eén keer per ongeluk schrijven bederft alles dat ernaar verwijst.

Het beheersen van pointers is niet optioneel. Het is het verschil tussen het schrijven van C en het schrijven in C. Oefen totdat de geheugenindeling intuïtief aanvoelt. Totdat je de stapel en de heap kunt zien zonder naar een diagram te kijken.

De taalkenmerken die we tot nu toe hebben besproken, bestaan ​​in de meeste moderne talen. Python heeft referenties. Java heeft objecthandvatten. C++ heeft slimme verwijzingen. Maar C vraagt ​​iets anders. Het vereist dat je het geheugen zelf beheert. Geen vuilnisman om de rommel op te ruimen. Geen runtime om lekken te voorkomen. Vervolgens bekijken we hoe C u dwingt om geheugentoewijzing en -deallocatie met chirurgische precisie aan te pakken.

Waarom geheugencontrole nog steeds belangrijk is in C

De veelzijdigheid van C komt voort uit het vermogen om te verkleinen. Met deze taal kun je een programma strippen totdat het op minimale hardware draait. Dit was niet alleen maar nostalgie. Vroege computers waren zwak. Tegenwoordig brengt de vraag naar elektronica met een laag vermogen – mobiele telefoons, kleine medische apparaten – die beperking terug. C blijft de beste keuze als u gedetailleerde controle over het geheugengebruik nodig heeft.

Dit begrijpen begint met hoe een programma in RAM leeft. Wanneer u een uitvoerbaar bestand start, wordt het in het geheugen geladen en begint het instructies uit te voeren via de processor. Functies worden in specifieke geheugenblokken geladen terwijl ze worden uitgevoerd. Ze verlaten die blokken als ze klaar zijn. Elk nieuw datapunt neemt ruimte in beslag gedurende de levensduur van het programma.

Dynamische opslagtoewijzing uitgelegd

Om deze chaos te beheersen, heb je dynamische opslagtoewijzing nodig. Dit betekent dat u alleen geheugen reserveert wanneer dat nodig is en het onmiddellijk daarna vrijmaakt. Veel talen verwerken dit automatisch met garbagecollection. C niet. C vereist expliciet beheer.

De standaardbibliotheek biedt de tools. Jij beheert de levenscyclus.

  • malloc : afkorting voor geheugentoewijzing. Het reserveert een geheugenblok van een specifieke grootte. Het retourneert een verwijzing naar dat blok. Je gebruikt dit voor datastructuren zoals arrays, niet voor afzonderlijke gehele getallen zoals int i.
  • calloc : vergelijkbaar met malloc, maar het geheugen wordt ook gewist bij reservering.
  • realloc : wijzigt de grootte van een eerder toegewezen geheugenblok.
  • gratis : dwingt het programma om het geheugen vrij te geven dat aan een specifieke aanwijzer is toegewezen.

De beste praktijk is eenvoudig: toewijzen met malloc, gratis met gratis.

Als u geheugen toewijst, zelfs tijdelijk, blijft het in het RAM-geheugen totdat het besturingssysteem is opgeschoond. Om de footprint klein te houden, maakt u het geheugen vrij voordat de functie wordt afgesloten. Dit voorkomt geheugenlekken. Een geheugenlek treedt op wanneer een programma steeds meer geheugen in beslag neemt totdat het vastloopt of crasht. Maak echter niet later vrij wat je nodig hebt in dezelfde functie. Gegevens verliezen is erger dan een lek.

C versus C++ en kernconcepten

Is C hetzelfde als C++? Nee. C++ breidt C uit. Het is niet dezelfde taal. C-programmeren is een concept voor het maken van draagbare, efficiënte software. Het is gebaseerd op de C-taal die in de jaren zeventig bij Bell Labs is ontwikkeld.

C is krachtig en veelzijdig. Het is geschikt voor verschillende toepassingen. Als u de structuur en geschiedenis ervan begrijpt, wordt duidelijk waarom het uniek blijft. De taal biedt functies die andere talen missen.

Voor meer begeleiding kunt u aanvullende programmeerbronnen verkennen. Deze zullen je reis naar C. verdiepen.