How to Work with Variables, Data Types, and Arithmetic Expressions in the C Programming Language

C heeft een rijke variatie aan rekenkundige operatoren die je kunt gebruiken om je gegevens te manipuleren. In dit hoofdstuk uit Programming in C, 4th Edition, behandelt Stephen G. Kochan de datatypes int, float, double, char, en _Bool, het wijzigen van datatypes met short, long, en long long, de regels voor het benoemen van variabelen, basis wiskunde operatoren en rekenkundige expressies, en type casting.

De ware kracht van programma’s die je maakt is hun manipulatie van gegevens. Om echt te profiteren van deze kracht, moet je de verschillende datatypes die je kunt gebruiken beter begrijpen, evenals hoe je variabelen kunt maken en benoemen. C heeft een rijke variëteit aan wiskunde operatoren die je kunt gebruiken om je gegevens te manipuleren. In dit hoofdstuk behandelen we:

  • De gegevenstypen int, float, double, char, en _Bool
  • Het wijzigen van gegevenstypen met short, long, en lang lang
  • De regels voor het benoemen van variabelen
  • Basis wiskunde operatoren en rekenkundige expressies
  • Type casting

Inzicht in gegevenstypen en constanten

U heeft al kennis gemaakt met het C basis gegevenstype int. Zoals u zich zult herinneren, kan een variabele van het type int alleen worden gebruikt om integrale waarden te bevatten, d.w.z. waarden zonder decimalen.

De programmeertaal C kent vier andere basistypen: float, double, char, en _Bool. Een variabele van het type float kan worden gebruikt voor het opslaan van floating-point getallen (waarden met decimalen). Het type double is hetzelfde als het type float, alleen met ongeveer tweemaal de precisie. Het gegevenstype char kan worden gebruikt om een enkel karakter op te slaan, zoals de letter “a”, het cijferteken “6”, of een puntkomma (“;”) (later meer hierover). Tenslotte kan het gegevenstype _Bool worden gebruikt om alleen de waarden 0 of 1 op te slaan. Variabelen van dit type worden gebruikt voor het aangeven van een aan/uit, ja/nee, of waar/waar situatie. Deze een-of-andere keuzes staan ook bekend als binaire keuzes.

In C wordt elk getal, enkel karakter, of tekenreeks een constante genoemd. Bijvoorbeeld, het getal 58 vertegenwoordigt een constante gehele waarde. De tekenreeks “Programmeren in C is leuk” is een voorbeeld van een constante tekenreeks. Uitdrukkingen die volledig bestaan uit constante waarden worden constante uitdrukkingen genoemd. Dus, de uitdrukking

128 + 7 - 17

is een constante uitdrukking omdat elk van de termen van de uitdrukking een constante waarde is. Maar als i zou worden gedeclareerd als een integer variabele, zou de uitdrukking

128 + 7 – i

geen constante uitdrukking zijn, omdat de waarde ervan zou veranderen op basis van de waarde van i. Als i 10 is, is de uitdrukking gelijk aan 125, maar als i 200 is, is de uitdrukking gelijk aan -65.

Het integer type int

In C bestaat een integer constante uit een reeks van een of meer cijfers. Een minteken voor de reeks geeft aan dat de waarde negatief is. De waarden 158, -10, en 0 zijn allemaal geldige voorbeelden van gehele constanten. Spaties tussen de cijfers zijn niet toegestaan, en waarden groter dan 999 kunnen niet worden uitgedrukt met komma’s. (Dus, de waarde 12.000 is geen geldige gehele constante en moet worden geschreven als 12000.)

Twee speciale formaten in C maken het mogelijk gehele constanten uit te drukken in een andere basis dan decimaal (basis 10). Als het eerste cijfer van de gehele waarde een 0 is, wordt het gehele getal in octale notatie opgevat, dat wil zeggen in basis 8. In dat geval moeten de overige cijfers van de waarde geldige cijfers van basis-8 zijn en dus 0-7. Dus, om de waarde 50 in basis 8 in C uit te drukken, die gelijk is aan de waarde 40 in decimaal, wordt de notatie 050 gebruikt. Op dezelfde manier vertegenwoordigt de octale constante 0177 de decimale waarde 127 (1 × 64 + 7 × 8 + 7). Een gehele waarde kan op de terminal worden weergegeven in octale notatie door gebruik te maken van de format tekens %o in de format string van een printf() statement. In zo’n geval wordt de waarde weergegeven in octaal zonder een voorloopnul. Het format karakter %#o zorgt er wel voor dat een voorloopnul wordt weergegeven voor een octale waarde.

Als een gehele constante wordt voorafgegaan door een nul en de letter x (hoofd- of kleine letters), wordt de waarde beschouwd als zijnde uitgedrukt in hexadecimale (basis 16) notatie. Onmiddellijk na de letter x volgen de cijfers van de hexadecimale waarde, die kan bestaan uit de cijfers 0-9 en de letters a-f (of A-F). De letters staan respectievelijk voor de waarden 10-15. Dus, om de hexadecimale waarde FFEF0D toe te kennen aan een integer variabele genaamd rgbColor, kan het statement

rgbColor = 0xFFEF0D;

worden gebruikt. De format tekens %x geven een waarde weer in hexadecimaal formaat zonder de voorloop 0x, en met gebruik van kleine letters a-f voor hexadecimale cijfers. Om de waarde met de voorloop 0x weer te geven, gebruikt u de format-tekens %#x, zoals in het volgende:

printf ("Color is %#x\n", rgbColor);

Een hoofdletter x, zoals in %X of %#X, kan worden gebruikt om de voorloop x en de daaropvolgende hexadecimale cijfers met hoofdletters weer te geven.

Opgeslagen grootten en bereiken

Elke waarde, of het nu een teken, een geheel getal of een drijvend-kommagetal is, heeft een bereik van waarden die ermee geassocieerd zijn. Dit bereik heeft te maken met de hoeveelheid opslagruimte die wordt toegewezen om een bepaald type gegevens op te slaan. In het algemeen is die hoeveelheid niet gedefinieerd in de taal. Het hangt typisch af van de computer waarop je draait, en wordt daarom implementatie- of machine-afhankelijk genoemd. Bijvoorbeeld, een geheel getal kan 32 bits in beslag nemen op uw computer, of misschien wordt het opgeslagen in 64 bits. U moet nooit programma’s schrijven die aannames maken over de grootte van uw datatypes. Er wordt echter gegarandeerd dat een minimale hoeveelheid opslagruimte wordt gereserveerd voor elk basistype. Zo is het bijvoorbeeld gegarandeerd dat een geheel getal wordt opgeslagen in een minimum van 32 bits, wat op veel computers de grootte van een “woord” is.

Het Floating Number Type float

Een variabele van het type float kan worden gebruikt voor het opslaan van waarden met decimalen. Een drijvende-komma constante onderscheidt zich door de aanwezigheid van een decimaalteken. U kunt cijfers voor de decimale punt of cijfers achter de decimale punt weglaten, maar uiteraard niet beide. De waarden 3., 125.8, en -.0001 zijn allemaal geldige voorbeelden van drijvende-komma constanten. Om een floating-point waarde op de terminal weer te geven, worden de printf conversietekens %f gebruikt.

Floating-point constanten kunnen ook worden uitgedrukt in wetenschappelijke notatie. De waarde 1,7e4 is een drijvende-kommawaarde uitgedrukt in deze notatie en vertegenwoordigt de waarde 1,7 × 104. De waarde vóór de letter e wordt de mantisse genoemd, terwijl de waarde die volgt de exponent wordt genoemd. Deze exponent, die kan worden voorafgegaan door een facultatief plus- of minteken, vertegenwoordigt de macht van 10 waarmee de mantisse moet worden vermenigvuldigd. In de constante 2.25e-3 is dus 2.25 de waarde van de mantisse en -3 de waarde van de exponent. Deze constante vertegenwoordigt de waarde 2,25 × 10-3, ofwel 0,00225. Overigens kan de letter e, die de mantisse van de exponent scheidt, zowel in kleine letters als in hoofdletters worden geschreven.

Om een waarde in wetenschappelijke notatie weer te geven, moeten de format characters %e in de printf() format string worden gespecificeerd. De printf() formattekens %g kunnen worden gebruikt om printf() te laten beslissen of de drijvende-kommawaarde in de normale drijvende-komma-notatie of in de wetenschappelijke notatie moet worden weergegeven. Deze beslissing is gebaseerd op de waarde van de exponent: Als deze kleiner is dan -4 of groter dan 5, wordt %e (wetenschappelijke notatie) formaat gebruikt; anders wordt %f formaat gebruikt.

Gebruik de %g formaattekens voor het weergeven van drijvende-komma getallen-het produceert de meest esthetisch aangename uitvoer.

Een hexadecimale drijvende constante bestaat uit een leidende 0x of 0X, gevolgd door een of meer decimale of hexadecimale cijfers, gevolgd door een p of P, gevolgd door een naar keuze ondertekende binaire exponent. Bijvoorbeeld, 0x0.3p10 vertegenwoordigt de waarde 3/16 × 210 = 0.5.

Het uitgebreide precisietype double

Het type double lijkt veel op het type float, maar het wordt gebruikt wanneer het bereik van een float-variabele niet voldoende is. Variabelen van het type double kunnen ongeveer twee keer zoveel significante cijfers opslaan als een variabele van het type float. De meeste computers geven dubbele waarden weer met 64 bits.

Tenzij anders aangegeven, worden alle floating-point constanten door de C compiler als dubbele waarden aangenomen. Om een float-constante expliciet weer te geven, voegt u een f of F toe aan het eind van het getal, als volgt:

12.5f

Om een dubbele waarde weer te geven, kunnen de formattekens %f, %e, of %g worden gebruikt, die dezelfde formattekens zijn die worden gebruikt om een float-waarde weer te geven.

Het enkele teken type char

Een char-variabele kan worden gebruikt om een enkel teken op te slaan.1 Een tekenconstante wordt gevormd door het teken binnen een paar enkele aanhalingstekens te plaatsen. Dus ‘a’, ‘;’, en ‘0’ zijn allemaal geldige voorbeelden van karakterconstanten. De eerste constante stelt de letter a voor, de tweede is een puntkomma, en de derde is het teken nul – wat niet hetzelfde is als het getal nul. Verwar een tekenconstante, dat is een enkel teken tussen enkele aanhalingstekens, niet met een tekenreeks, dat is een willekeurig aantal tekens tussen dubbele aanhalingstekens.

De tekenconstante ‘\n’-het teken newline-is een geldige tekenconstante, ook al lijkt deze in tegenspraak met de eerder genoemde regel. Dit komt omdat het backslash karakter een speciaal karakter is in het C systeem en eigenlijk niet telt als een karakter. Met andere woorden, de C compiler behandelt het karakter “\n” als een enkel karakter, ook al wordt het eigenlijk gevormd door twee karakters. Er zijn andere speciale karakters die met het backslash karakter worden geïnitieerd. Raadpleeg Appendix A, “C Language Summary,” voor een volledige lijst.

De format-tekens %c kunnen worden gebruikt in een printf()-aanroep om de waarde van een char-variabele op de terminal weer te geven.

Het Booleaanse gegevenstype _Bool

Een _Bool-variabele wordt in de taal gedefinieerd als groot genoeg om alleen de waarden 0 en 1 op te slaan. De precieze hoeveelheid geheugen die wordt gebruikt, is niet gespecificeerd. _Bool-variabelen worden gebruikt in programma’s die een Booleaanse toestand moeten aangeven. Een variabele van dit type kan bijvoorbeeld worden gebruikt om aan te geven of alle gegevens uit een bestand zijn gelezen.

Overeengekomen wordt 0 gebruikt om een onware waarde aan te geven, en 1 om een ware waarde aan te geven. Bij het toewijzen van een waarde aan een _Bool-variabele, wordt een waarde van 0 opgeslagen als 0 in de variabele, terwijl elke niet-nul waarde wordt opgeslagen als 1.

Om het werken met _Bool-variabelen in uw programma te vergemakkelijken, definieert het standaard header-bestand <stdbool.h> de waarden bool, true, en false. Een voorbeeld hiervan is te zien in programma 5.10A in hoofdstuk 5, “Beslissingen nemen.”

In programma 3.1 worden de basisgegevenstypen van C gebruikt.

Programma 3.1 Gebruik van de basisgegevenstypen

Programma 3.1 Uitvoer

integerVar = 100floatingVar = 331.790009doubleVar = 8.440000e+11doubleVar = 8.44e+11charVar = WboolVar = 0;

De eerste instructie van programma 3.1 verklaart de variabele integerVar tot een integer variabele en kent er ook een beginwaarde van 100 aan toe, alsof in plaats daarvan de volgende twee verklaringen waren gebruikt:

int integerVar;integerVar = 100;

In de tweede regel van de uitvoer van het programma ziet u dat de waarde van 331,79, die is toegewezen aan floatingVar, in werkelijkheid wordt weergegeven als 331,790009. In feite is de werkelijke waarde die wordt weergegeven afhankelijk van het specifieke computersysteem dat u gebruikt. De reden voor deze onnauwkeurigheid is de specifieke manier waarop getallen intern in de computer worden weergegeven. U bent waarschijnlijk dezelfde soort onnauwkeurigheid tegengekomen wanneer u met getallen op uw zakrekenmachine werkte. Als u op uw rekenmachine 1 deelt door 3, krijgt u het resultaat .33333333, met misschien nog wat extra 3-en aan het eind. De reeks van 3-en is de benadering van de rekenmachine voor een derde. Theoretisch zou er een oneindig aantal 3’s moeten zijn. Maar de rekenmachine kan maar een beperkt aantal cijfers bevatten, dus de inherente onnauwkeurigheid van de machine. Dezelfde soort onnauwkeurigheid is hier van toepassing: Bepaalde floating-point waarden kunnen niet exact worden weergegeven in het geheugen van de computer.

Bij het weergeven van de waarden van float- of dubbele variabelen, heeft u de keuze uit drie verschillende formaten. De tekens %f worden gebruikt om waarden op een standaardmanier weer te geven. Tenzij anders aangegeven, geeft printf() altijd een float of double waarde weer met zes decimalen afgerond. Later in dit hoofdstuk ziet u hoe u het aantal decimalen kunt selecteren dat wordt weergegeven.

De %e-tekens worden gebruikt om de waarde van een zwevende of dubbele variabele in wetenschappelijke notatie weer te geven. Ook hier worden zes decimalen automatisch door het systeem weergegeven.

Met de %g-tekens kiest printf() tussen %f en %e en verwijdert het ook automatisch alle nullen achter de komma uit de weergave. Als er geen cijfers achter de komma staan, wordt dat ook niet weergegeven.

In het een-na-laatste printf() statement worden de %c-tekens gebruikt om het enkele teken ‘W’ weer te geven dat u aan charVar hebt toegewezen toen de variabele werd gedeclareerd. Onthoud dat, terwijl een tekenreeks (zoals het eerste argument voor printf()) wordt ingesloten door een paar dubbele aanhalingstekens, een tekenconstante altijd moet worden ingesloten door een paar enkele aanhalingstekens.

De laatste printf() laat zien dat de waarde van een _Bool-variabele kan worden weergegeven met behulp van de integer-formaattekens %i.

Type Specifiers: long, long long, short, unsigned, and signed

Als de specifier long direct voor de int-declaratie wordt geplaatst, heeft de gedeclareerde integer-variabele op sommige computersystemen een groter bereik. Een voorbeeld van een lange int declaratie zou kunnen zijn

long int factorial;

Dit declareert de variabele factorial als een lange gehele variabele. Net als bij floats en doubles hangt de nauwkeurigheid van een lange variabele af van uw computersysteem. Op veel systemen hebben een int en een lange int hetzelfde bereik en kunnen beide worden gebruikt om gehele waarden op te slaan tot 32 bits breed (231 – 1, of 2.147.483.647).

Een constante waarde van het type lange int wordt gevormd door naar keuze de letter L (hoofd- of kleine letters) toe te voegen aan het einde van een gehele constante. Tussen het getal en de L mogen geen spaties staan. In de declaratie

long int numberOfPoints = 131071100L;

wordt de variabele numberOfPoints dus van het type long int met een beginwaarde van 131.071.100.

Om de waarde van een long int met printf() weer te geven, wordt de letter l gebruikt als modifier vóór de integer-formaattekens i, o, en x. Dit betekent dat de format tekens %li kunnen worden gebruikt om de waarde van een lange int in decimaal formaat weer te geven, de tekens %lo kunnen de waarde in octaal formaat weergeven, en de tekens %lx kunnen de waarde in hexadecimaal formaat weergeven.

Er is ook een lang lang geheel getal datatype, dus

long long int maxAllowedStorage;

de aangegeven variabele wordt aangegeven met de gespecificeerde uitgebreide nauwkeurigheid, die gegarandeerd ten minste 64 bits breed is. In plaats van een enkele letter l, worden twee ls gebruikt in de printf string om lange lange gehele getallen weer te geven, zoals in “%lli”.

De lange specificator is ook toegestaan voor een dubbele declaratie, als volgt:

long double US_deficit_2004;

Een lange dubbele constante wordt geschreven als een drijvende constante met de letter l of L onmiddellijk gevolgd, zoals

1.234e+7L

Om een lang dubbel weer te geven, wordt de L modifier gebruikt. Dus, %Lf geeft een lange dubbele waarde weer in floating-point notatie, %Le geeft dezelfde waarde weer in scientific notatie, en %Lg vertelt printf() te kiezen tussen %Lf en %Le.

De specificier short, wanneer geplaatst voor de int declaratie, vertelt de C compiler dat de specifieke variabele die wordt gedeclareerd wordt gebruikt om redelijk kleine integer waarden op te slaan. De motivatie voor het gebruik van korte variabelen is er in de eerste plaats een van het besparen van geheugenruimte, wat een probleem kan zijn in situaties waarin het programma veel geheugen nodig heeft en de hoeveelheid beschikbaar geheugen beperkt is.

Op sommige machines neemt een korte int de helft van de hoeveelheid opslagruimte in beslag als een gewone int variabele doet. In elk geval bent u er zeker van dat de hoeveelheid ruimte die voor een korte int wordt toegewezen, niet minder dan 16 bits zal zijn.

Er is geen manier om expliciet een constante van het type korte int in C te schrijven. Om een korte int variabele weer te geven, plaatst u de letter h voor een van de normale integer conversietekens: %hi, %ho, of %hx. Als alternatief kunt u ook een van de integer conversietekens gebruiken om korte ints weer te geven, vanwege de manier waarop ze kunnen worden geconverteerd in gehele getallen wanneer ze worden doorgegeven als argumenten aan de printf() routine.

De laatste specificatie die voor een int variabele kan worden geplaatst, wordt gebruikt wanneer een integer variabele zal worden gebruikt om alleen positieve getallen op te slaan. De declaratie

unsigned int counter;

verklaart aan de compiler dat de variabele teller wordt gebruikt om alleen positieve waarden op te slaan. Door het gebruik van een integer variabele te beperken tot de exclusieve opslag van positieve gehele getallen, wordt de nauwkeurigheid van de integer variabele vergroot.

Een niet-ondertekende int constante wordt gevormd door de letter u (of U) achter de constante te plaatsen, als volgt:

0x00ffU

U kunt de letters u (of U) en l (of L) combineren bij het schrijven van een integer constante, dus

20000UL

vertelt de compiler om de constante 20000 te behandelen als een niet-ondertekende lange.

Een gehele constante die niet wordt gevolgd door een van de letters u, U, l, of L en die te groot is om in een int van normale grootte te passen, wordt door de compiler behandeld als een niet-ondertekende int. Als het te klein is om in een niet-ondertekende int te passen, behandelt de compiler het als een lange int. Als het nog steeds niet in een lange int past, maakt de compiler er een niet-ondertekende lange int van. Als het daar niet in past, behandelt de compiler het als een lange lange int als het past, en als een niet-ondertekende lange int anders.

Wanneer variabelen worden gedeclareerd als type lange lange int, lange int, korte int, of niet-ondertekende int, kunt u het sleutelwoord int weglaten. Daarom had de variabele teller zonder tekens ook als volgt kunnen worden gedeclareerd:

unsigned counter;

U kunt char-variabelen ook zonder tekens declareren.

De kwalificator ondertekend kan worden gebruikt om de compiler expliciet te vertellen dat een bepaalde variabele een ondertekende grootheid is. Het gebruik ervan is voornamelijk voor de char-declaratie, en verdere bespreking wordt uitgesteld tot Hoofdstuk 13, “Meer over Gegevenstypen.”

Maak je geen zorgen als de besprekingen van deze specifiers je op dit moment een beetje esoterisch lijken. In latere secties van dit boek worden veel van deze verschillende types geïllustreerd met echte programmavoorbeelden. Hoofdstuk 13 gaat dieper in op datatypes en conversies.

Tabel 3.1 geeft een overzicht van de basis datatypes en qualifiers.

Tabel 3.1 Basis Data Types