Basis van C: typen, operatoren en programmaverloop

Schrijf je eerste C-programma’s en begrijp precies wat ze doen. Compileer code met gcc of clang, voer die uit en herstel wat de compiler meldt. Sla gegevens op in variabelen voor gehele getallen, tekens en drijvendekommagetallen en schrijf en lees waarden met printf en scanf. Bouw expressies met de rekenkundige, vergelijkings-, logische en bitsgewijze operatoren van C en voorspel conversies en operatorprioriteit. Stuur vervolgens een programma met if, switch, while, do-while en for. Leer tot slot waarom overloop bij gehele getallen optreedt, wat er dan gebeurt en hoe je code schrijft en test die dit voorkomt.

BeginnerLes 1 van 8 in Cursus Programmeren in C: pointers, geheugenbeheer en debuggen

Wat je leert

  1. Zet C-broncode om in een werkend programma, weet wat de compiler in elke fase doet en gebruik zijn meldingen om fouten op te sporen en te herstellen.
    • Schrijf, compileer en voer je eerste C-programma uit
      Lees de onderdelen van een minimaal C-programma, compileer het met gcc of clang en voer het uitvoerbare bestand uit vanuit een terminal.
    • Lees compilermeldingen en herstel fouten
      Onderscheid compilatiefouten, waarschuwingen, koppelingsfouten, uitvoeringsfouten en logische fouten en gebruik compilermeldingen en proefuitvoeringen om ze te herstellen.
  2. Declareer en initialiseer variabelen, kies tussen de geheelgetaltypen en drijvendekommatypen van C en voorspel het waardenbereik en de precisie van elk type.
    • Declareer en initialiseer variabelen en geef ze een naam
      Maak variabelen met een type en een betekenisvolle naam, geef ze beginwaarden en gebruik constanten voor vaste waarden.
    • Kies een geheelgetaltype en ken het waardenbereik
      Kies tussen de geheelgetaltypen met en zonder teken, tekenwaarden en Booleaanse waarden van C aan de hand van hun grootten, waardenbereiken en literalvormen.
    • Werk met drijvendekommagetallen en hun beperkingen
      Sla reële getallen op in float en double, voorspel hun precisie en afrondingsfouten en converteer veilig tussen drijvendekommawaarden en gehele getallen.
  3. Maak getallen en tekst nauwkeurig op met printf en lees en controleer met scanf waarden die de gebruiker typt.
    • Maak uitvoer op met printf
      Gebruik conversiespecificaties, veldbreedten, precisie, vlaggen en escapesequenties om precies de bedoelde uitvoer te maken.
    • Lees en controleer invoer met scanf
      Lees getallen en tekens die de gebruiker typt, controleer of het lezen is geslaagd en herstel na ongeldige invoer.
  4. Gebruik de rekenkundige, toewijzings-, vergelijkings-, logische en bitsgewijze operatoren van C en voorspel de waarde en het type van elke expressie op basis van conversies, operatorprioriteit en associativiteit.
    • Reken met rekenkundige operatoren en toewijzingsoperatoren
      Pas +, -, *, / en %, geheeltallige deling, samengestelde toewijzing, incrementatie en decrementatie toe en vermijd de gebruikelijke valkuilen.
    • Voorspel typeconversies in expressies met verschillende typen
      Leg uit hoe C operanden van verschillende typen converteert, gebruik expliciete typeconversies bewust en vermijd de valkuil van typen met en zonder teken.
    • Combineer voorwaarden met relationele, logische en bitsgewijze operatoren
      Schrijf correcte vergelijkingen en samengestelde voorwaarden, gebruik kortsluitevaluatie en bewerk afzonderlijke bits.
    • Pas operatorprioriteit, associativiteit en evaluatievolgorde toe
      Voorspel hoe C de operatoren in een expressie groepeert, evalueer expressies stap voor stap en gebruik haakjes waar de groepering onduidelijk is.
  5. Kies tussen alternatieven met if, else en switch en herhaal werk met while-, do-while- en for-lussen die precies volgens de bedoeling beginnen, draaien en stoppen.
    • Maak keuzes met if en else
      Schrijf if-, else if- en else-instructies met correcte voorwaarden en accolades en vermijd de gebruikelijke valkuilen van de syntaxis van C.
    • Kies uit verschillende gevallen met switch
      Gebruik switch met case-labels, break en default om uit vaste gehele waarden te kiezen en laat alleen bewust doorvallen.
    • Herhaal zolang een voorwaarde waar is met while en do-while
      Schrijf lussen die door een voorwaarde worden gestuurd, correct beginnen, voortgang maken en stoppen, waaronder invoerlussen en lussen die minstens eenmaal moeten worden uitgevoerd.
    • Tel met for-lussen, nest lussen en stop voortijdig
      Schrijf for-lussen die precies het bedoelde aantal keren worden uitgevoerd, combineer lussen binnen lussen en gebruik break en continue bewust.
  6. Leg uit wat er gebeurt als een resultaat niet in het geheelgetaltype past en schrijf en test code die overloop voorkomt of detecteert.
    • Leg uit wat er gebeurt als een waarde niet in het type past
      Beschrijf modulaire terugloop bij gehele getallen zonder teken, ongedefinieerde overloop bij gehele getallen met teken en conversies buiten het waardenbereik en verbind die met werkelijke fouten.
    • Schrijf code die overloop voorkomt of detecteert
      Kies typen met voldoende bereik, controleer bewerkingen voordat ze overlopen en gebruik compilerhulpmiddelen en grenswaardetests om overloop vroeg op te sporen.

Typen en operatoren in C: uitleg

6 hoofdstukken

1

Bouw C-programma’s en voer ze uit

C-code wordt door een bouwproces een werkend programma. Leer de onderdelen van een volledig eerste programma, compileer het met een gekozen taalstandaard en voer het resulterende uitvoerbare bestand uit. Als een bouwproces of test misgaat, gebruik je de fase waarin de fout optreedt en de eerste compilermelding om de oorzaak te vinden. Door de cyclus van bewerken, bouwen en testen zorgvuldig te volgen, voorkom je dat een oud uitvoerbaar bestand een mislukte wijziging verhult en kun je elke reparatie gemakkelijker controleren.

1.1 Van broncode naar een werkend programma

Een C-programma begint als tekst zonder opmaak en wordt uitgevoerd als machinecode. De compiler verbindt die twee vormen. Als je een wijziging opslaat, verandert de broncode; het eerder gebouwde uitvoerbare bestand verandert niet.

Lees een volledig programma

Sla dit op als hello.c:

#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

#include <stdio.h> maakt declaraties voor standaardinvoer en -uitvoer beschikbaar, waaronder die van printf. De uitvoering begint in main. In int main(void) noemt int het retourtype en betekent void dat er geen parameters zijn. Accolades omsluiten de functie-inhoud. De aanroep van printf schrijft de tekst tussen aanhalingstekens; \n gaat naar een nieuwe regel. return 0; beëindigt het programma en meldt aan het besturingssysteem dat de uitvoering is geslaagd. Een status anders dan nul kan een fout melden. Sinds C99 geeft ook het bereiken van het einde van main nul terug.

Eenvoudige instructies eindigen met een puntkomma. De include-regel niet. C maakt onderscheid tussen hoofdletters en kleine letters: Printf en printf zijn verschillende namen. Spaties tussen tokens beïnvloeden het programma meestal niet, maar consequent inspringen maakt de blokstructuur zichtbaar.

Compileer en voer uit

Open in de terminal de map met de broncode en voer in:

gcc -std=c17 -Wall -Wextra hello.c -o hello
./hello

De eerste opdracht bouwt een uitvoerbaar bestand met de naam hello; de tweede voert het uit vanuit de huidige map. De uitvoer is Hello, world! gevolgd door een nieuweregelteken. Je kunt clang gebruiken in plaats van gcc. Zonder -o hello is de gebruikelijke naam van de uitvoer op Linux of macOS a.out. Uitvoerbare bestanden op Windows gebruiken .exe. make hello van CS50 is een verkorte opdracht die het bouwhulpmiddel vraagt de compiler aan te roepen.

Nadat je de begroeting hebt gewijzigd, sla je op, compileer je opnieuw en voer je het programma opnieuw uit. Als de compilatie mislukt, kan een ouder uitvoerbaar bestand nog aanwezig zijn. Als je dat uitvoert, zie je het oude gedrag. Controleer daarom of het bouwen is geslaagd voordat je je wijziging beoordeelt.

Wat het bouwproces doet

De preprocessor verwerkt richtlijnen zoals #include en voegt headertekst in. De compiler zelf vertaalt C naar assembleertaal. De assembler maakt objectcode, die nog niet-opgeloste namen kan bevatten. De linker combineert objectcode en lost verwijzingen zoals printf op. Bibliotheekcode kan door statisch koppelen naar een uitvoerbaar bestand worden gekopieerd of worden geleverd via een gedeelde bibliotheek die wordt geladen wanneer het programma draait.

Je kunt het bouwproces in tussenfasen stoppen:

OptieResultaat
-EVoorbewerkte tekst
-SAssembleertaal
-cEen objectbestand, zoals hello.o

Een objectbestand is nog niet het voltooide uitvoerbare bestand. Een uitvoerbaar bestand kan zonder de compiler draaien, maar is gebouwd voor een bepaald besturingssysteem en een bepaalde processor. Om het programma naar een ander platform over te brengen, compileer je de broncode daar.

Commentaar en taalversies

// begint commentaar dat eindigt bij het nieuweregelteken. /* ... */ kan meerdere regels beslaan, maar deze commentaren kunnen niet worden genest: de eerste */ beëindigt het commentaar. Gebruik commentaar om een beslissing uit te leggen die de code zelf onduidelijk laat.

Kies de taalversie expliciet. C17 ondersteunt de voorbeelden hierboven, waaronder commentaar met //, long long en declaraties in de kop van for-lussen. C23 voegt voorzieningen toe zoals bool als sleutelwoord; deze versie is gepubliceerd als ISO/IEC 9899:2024. -Wall -Wextra schakelt nuttige waarschuwingen in. De standaardinstellingen van compilers verschillen, dus met een expliciete -std=c17 zijn de bedoelde taalregels duidelijk.

Belangrijkste punten

  • C-broncode moet na elke wijziging opnieuw worden gecompileerd.
  • De uitvoering begint in main; nul teruggeven meldt dat de uitvoering is geslaagd.
  • De bouwfasen zijn voorbewerking, compilatie, assemblage en koppeling.
  • Gebruik -std=c17 -Wall -Wextra om de standaard te kiezen en waarschuwingen in te schakelen.

1.2 De eerste oorzaak van een fout vinden

Het moment waarop een probleem verschijnt, helpt je de oorzaak te vinden. Een programma kan niet worden gebouwd, tijdens de uitvoering stoppen of met het verkeerde antwoord eindigen. Die situaties vragen om verschillende controles.

Onderscheid de soorten problemen
SoortWat je zietWaar je kijkt
CompilatiefoutGeen nieuw uitvoerbaar bestandSyntaxis, declaraties en typen
WaarschuwingEen mogelijk probleem; het bouwen gaat meestal doorDe expressie die in de melding wordt genoemd
KoppelingsfoutObjectcode bestaat, maar het uitvoerbare bestand kan niet worden voltooidNiet-opgeloste functienamen of vereiste bibliotheken
UitvoeringsfoutEen probleem verschijnt tijdens de uitvoeringBewerkingen en waarden die bij die invoer worden bereikt
Logische foutHet programma draait maar geeft een onbedoeld antwoordVoorwaarden, formules en bijwerkingen

Een waarschuwing kan zowel ongeldige code of ongedefinieerd gedrag als een gewone fout aanwijzen. Een geslaagde compilatie garandeert geen correct programma.

Lees een diagnostische melding

Een compilermelding geeft normaal gesproken een locatie in de broncode, een ernstniveau en een beschrijving. Een melding zoals hello.c:5:16: error: expected ';' after expression wijst op een ontbrekende puntkomma rond regel 5. Soms merkt de compiler het probleem op de volgende regel op. Bekijk de gemelde regel en de regel direct ervoor.

Herstel eerst de eerste fout en compileer daarna opnieuw. Een niet-afgesloten aanhalingsteken kan ervoor zorgen dat latere regels deel van een string lijken; een niet-overeenkomende accolade kan latere declaraties in het verkeerde blok doen lijken te staan. Na één reparatie kunnen meerdere meldingen verdwijnen. Begin niet met het wijzigen van elke regel die in de uitvoer wordt genoemd.

Voor dit fragment:

int total = 6
printf("%d\n", total);

De declaratie heeft een puntkomma nodig na 6. De regel met printf kan de plaats zijn waar de compiler voor het eerst merkt dat de vorige declaratie niet is geëindigd. Controleer ook de exacte spelling, overeenkomende haakjes en accolades en de header die een bibliotheekfunctie declareert. Een aanroep van printf heeft <stdio.h> nodig; aanroepen van niet-gedeclareerde functies zijn in C99 en later niet toegestaan.

Neem waarschuwingen serieus

Bouw met -Wall -Wextra. Deze opties helpen bij het opsporen van een toewijzing in een voorwaarde, ongebruikte variabelen, vergelijkingen tussen waarden met en zonder teken en niet-overeenkomende uitvoerformaten. Zo vraagt printf("%d\n", 3.5); om een geheel getal maar geeft het een double door; het formaat converteert het argument niet voor je. Herstel het formaat naar %f of converteer de waarde bewust. Met -Werror laten waarschuwingen het bouwproces stoppen.

Begrijp ongedefinieerd gedrag

Ongedefinieerd gedrag betekent dat C geen eisen stelt aan het gedrag van het programma. Overloop bij gehele getallen met teken, geheeltallige deling door nul en sommige leesacties van niet-geïnitialiseerde variabelen kunnen dit veroorzaken. Een programma kan stoppen, een onverwachte waarde afdrukken of lijken te werken. Compilerinstellingen kunnen veranderen wat er gebeurt.

Een melding zoals “Floating point exception” kan op sommige systemen zelfs het gevolg zijn van geheeltallige deling door nul. Onderzoek de bewerking en invoer, niet alleen de formulering van een melding van het besturingssysteem. Controleer een deler voordat je de deling uitvoert.

Volg een verkeerd resultaat

Voorspel bij een logische fout een klein geval met de hand en vergelijk dat met de werkelijke uitvoer. Voeg tijdelijke printf-aanroepen toe om een lopend totaal of de waarde die een voorwaarde test te tonen. Verander steeds één oorzaak en bouw na elke wijziging opnieuw. Test gewone waarden en grenswaarden zoals nul, één, een negatieve invoer en de grootste verwachte invoer. Een uitvoeringsspoor laat zien waar het programma voor het eerst afwijkt van je voorspelling.

Belangrijkste punten

  • Herstel de oorzaak van de eerste diagnostische melding en bouw opnieuw voordat je latere meldingen onderzoekt.
  • Een waarschuwing kan ongedefinieerd gedrag aanwijzen; compilatie alleen bewijst niet dat een programma correct is.
  • Stem formaatconversies af op de typen van de argumenten.
  • Volg bij een verkeerd resultaat een kleine invoer en vergelijk tussenwaarden.
  • Test zowel grenswaarden als gewone invoer.
2

Sla gegevens op in variabelen van de basistypen

Een type bepaalt hoe een opgeslagen waarde wordt geïnterpreteerd en welke waarden het kan bevatten. Begin met declaraties, beginwaarden en de namen die binnen een blok beschikbaar zijn. Kies vervolgens geheelgetaltypen op basis van hun bereik en drijvendekommatypen op basis van zowel precisie als bereik. Ook tekencodes en Booleaanse waarden passen in dit geheel. Door literals te lezen en grenzen te controleren, leer je voorspellen wat een berekening kan voorstellen voordat je die schrijft.

2.1 Variabelen, namen en vaste waarden

Een variabele geeft een opgeslagen waarde een naam. Het type vertelt C hoe die opslag moet worden geïnterpreteerd. Je declareert het type voordat je de variabele gebruikt en de variabele houdt dat type gedurende de hele levensduur.

Geef elke variabele een beginwaarde
int count = 0;
double length_m = 1.8;
count = count + 1;

De eerste twee regels declareren en initialiseren variabelen. De derde wijst een nieuwe waarde toe aan een bestaande variabele: de instructie leest de huidige count, telt er één bij op en slaat het resultaat weer op. Een toewijzing is een actie, dus count = count + 1 heeft betekenis, ook al zou het geen wiskundige vergelijking zijn.

Initialisatie gebeurt in een declaratie; een toewijzing kan later gebeuren. In int a, b = 5; wordt alleen b geïnitialiseerd. Een lokale a heeft een onbepaalde waarde totdat je er een waarde in opslaat. Die lezen kan onvoorspelbaar gedrag opleveren, waaronder ongedefinieerd gedrag. Initialiseer tellers en totalen wanneer je ze declareert, bijvoorbeeld int total = 0;.

Een toewijzing kopieert de waarde op dat moment:

int width = 4;
int area = width * 3;
width = 7;
printf("%d %d\n", width, area);

Dit drukt 7 12 af. De toewijzing aan area berekende eenmaal 12. Die maakte geen formule die opnieuw rekent telkens wanneer width verandert. Om area bij te werken, wijs je er een nieuwe berekening aan toe.

Kies leesbare namen

Een identifier kan letters, cijfers en onderstrepingstekens bevatten, maar mag niet met een cijfer beginnen of een sleutelwoord zijn. height_cm en second_place zijn geldig; 2nd_place niet en my-score wordt als een aftrekking gelezen. Namen zijn hoofdlettergevoelig, dus total en Total verschillen. Namen die een betekenis of eenheid bevatten, maken formules gemakkelijker te controleren.

Houd rekening met het geldigheidsgebied van een blok

Een lokale naam is beschikbaar vanaf de declaratie tot het einde van het blok. Sinds C99 mogen declaraties tussen andere instructies staan.

int count = 2;
{
    int count = 9;
    printf("%d\n", count);
}
printf("%d\n", count);

De binnenste declaratie verbergt de buitenste naam zolang dat blok wordt uitgevoerd. De uitvoer is 9 en daarna 2, op afzonderlijke regels. Dit zijn twee variabelen, geen twee toewijzingen aan één variabele. Dezelfde naam tweemaal in één blok declareren is een fout. Namen opnieuw gebruiken in geneste blokken is toegestaan, maar maakt code moeilijker te volgen. Kies daarom bij voorkeur namen die elke rol duidelijk houden.

Geef vaste waarden een naam
const int SECONDS_PER_MINUTE = 60;
#define MAX_HEIGHT 8

const maakt een getypeerde variabele waaraan je via die naam geen nieuwe waarde kunt toewijzen. Een latere instructie SECONDS_PER_MINUTE = 90; vereist een diagnostische melding. De initialisator geeft de vaste waarde bij de declaratie.

#define maakt een vervanging voor de preprocessor: latere voorkomens van MAX_HEIGHT worden vóór de compilatie vervangen door het token 8. Deze richtlijn bevat geen = en geen puntkomma. Een puntkomma zou deel worden van de vervanging en een expressie zoals height > MAX_HEIGHT ongeldig kunnen maken.

Een naam zoals SECONDS_PER_MINUTE vertelt een lezer waarom 60 in een berekening voorkomt en geeft je één plaats om de waarde bij te houden. Hoofdletters zijn een naamgevingsafspraak, geen regel die een waarde constant maakt. De declaratie of richtlijn zorgt voor dat gedrag.

Belangrijkste punten

  • Initialiseer lokale variabelen voordat je ze leest.
  • Een toewijzing kopieert eenmaal een waarde; afhankelijke waarden worden niet vanzelf bijgewerkt.
  • Een blok begrenst waar je een lokale naam kunt gebruiken.
  • Namen zijn hoofdlettergevoelig en mogen niet met een cijfer beginnen.
  • const levert een getypeerde alleen-lezenvariabele; #define levert een tokenvervanging.

2.2 Geheelgetaltypen, tekentypen en Booleaanse typen kiezen

Geheelgetaltypen slaan gehele getallen exact op binnen een vast bereik. Kies een type op basis van de waarden die het programma moet verwerken, inclusief tussenresultaten, en op basis van de vraag of negatieve waarden zinvol zijn.

Grootten en waardenbereiken

De standaard garandeert minimumbreedten: minstens 8 bits voor char, 16 voor short en int, 32 voor long en 64 voor long long. Elk type in deze reeks is minstens zo breed als het vorige. De werkelijke grootten hangen af van het platform.

TypeGebruikelijk aantal bytes op 64-bits Linux/macOSVerschil op 64-bits Windows
char1Geen
short2Geen
int4Geen
long84 bytes
long long8Geen

sizeof(int) geeft de grootte in bytes als een size_t; druk die af met %zu. De header <limits.h> levert namen zoals INT_MIN, INT_MAX, UINT_MAX, LLONG_MAX en CHAR_BIT, het aantal bits per byte.

Met N waardebits heeft een geheel getal zonder teken waarden van 0 tot en met 2ᴺ − 1. Een geheel getal met teken in tweecomplement met N bits loopt van −2ᴺ⁻¹ tot en met 2ᴺ⁻¹ − 1. Een 32-bits int loopt dus van −2.147.483.648 tot en met 2.147.483.647, terwijl een 32-bits unsigned int tot 4.294.967.295 gaat. C23 vereist tweecomplement; C17 stond ook andere representaties toe.

In een 8-bits patroon in tweecomplement heeft de hoogste bit het gewicht −128. 11111111 stelt −1 voor en 10000000 stelt −128 voor. Om −5 te vormen, begin je met 00000101, keer je de bits om tot 11111010 en tel je er één bij op om 11111011 te krijgen.

Tekens zijn ook gehele getallen

Een char slaat een tekencode op in één byte. Gebruik enkele aanhalingstekens voor een tekenconstante, zoals in 'A'. In ASCII heeft 'A' code 65 en '0' code 48. printf("%c %d\n", 'A', 'A'); drukt in ASCII A 65 af. Een tekenconstante in C heeft het type int, dus sizeof('A') is sizeof(int).

Gewoon char kan afhankelijk van de implementatie een type met of zonder teken zijn. Gebruik signed char of unsigned char wanneer het numerieke bereik van belang is. <stdint.h> biedt typen met exacte bitbreedte zoals int32_t en uint8_t op platforms die die exacte breedten ondersteunen; gangbare platforms doen dat.

Lees geheeltallige literals

Een decimale constante zoals 42 heeft het type int als de waarde daarin past. Achtervoegsels vragen om andere typen: 42u is zonder teken, 42L is long, 42LL is long long en 42ULL is unsigned long long. Hexadecimale notatie gebruikt 0x, zoals in 0x2A. Een voorloopnul betekent octaal: 052 betekent ook 42 en 010 betekent 8. 09 is ongeldig als octaal getal. C23 voegt binaire notatie toe, zoals 0b101010.

Waarheidswaarden en typekeuze

Neem in C17 <stdbool.h> op voor bool, true en false; C23 maakt er sleutelwoorden van. Een conversie naar bool geeft onwaar voor nul en waar voor elke waarde anders dan nul, waaronder 0.5. Druk een Booleaanse waarde af met %d om 0 of 1 te zien.

Gebruik int voor gewone aantallen en lustellers. Gebruik long long of een geschikt type met exacte bitbreedte wanneer het bereik boven ongeveer 2,1 miljard kan uitkomen. Typen zonder teken zijn geschikt voor bitpatronen, maar aftrekken tot onder nul loopt modulair terug naar een grote waarde, dus ook daarbij zijn bereikcontroles nodig.

Belangrijkste punten

  • Gebruik de typegrenzen in plaats van een geschatte grootte voor long.
  • Een 32-bits int met teken gaat tot 2.147.483.647.
  • Waarden zonder teken hebben geen negatief bereik en lopen bij onderloop modulair terug.
  • Een voorloopnul begint een octale geheeltallige literal.
  • Of gewoon char een teken heeft, hangt af van de implementatie.
  • In C17 komen Booleaanse namen uit <stdbool.h>.

2.3 Decimale waarden en beperkte precisie

Een drijvendekommatype slaat een benadering van een reëel getal op. Het heeft een groot bereik, maar slechts een vast aantal significante cijfers. Bereik en precisie beantwoorden verschillende vragen: hoe groot een waarde kan zijn en hoe nauwkeurig dicht bij elkaar liggende waarden kunnen worden onderscheiden.

Kies een drijvendekommatype

Op gangbare IEEE 754-systemen neemt float 4 bytes in en heeft het ongeveer 6–7 significante decimale cijfers. double neemt 8 bytes in en heeft er ongeveer 15–16. Hun grootste eindige waarden zijn ongeveer 3,4 × 10³⁸ en 1,8 × 10³⁰⁸. long double is minstens zo precies als double en heeft een formaat dat afhangt van het platform. double is een nuttige standaardkeuze voor gemeten grootheden.

Constanten zoals 2.0, 3.14 en 1e-3 zijn van het type double; 1e-3 betekent 0,001. Voeg f toe voor float, zoals in 2.5f, of L voor long double. De constante 2 is een geheel getal en kan dus leiden tot geheeltallige deling waar 2.0 een drijvendekommadeling zou opleveren.

Bekijk de opgeslagen benadering

Veel decimale breuken hebben geen exacte eindige binaire vorm. Op een gangbaar systeem met dubbele precisie:

printf("%.20f\n", 0.1);
printf("%.17f\n", 0.1 + 0.2);

De uitvoer is 0.10000000000000000555 en 0.30000000000000004. De extra cijfers tonen de opgeslagen benadering. Ze verklaren waarom 0.1 + 0.2 == 0.3 onwaar kan zijn.

Gebruik een tolerantie bij het vergelijken van berekende meetwaarden:

#include <math.h>
/* inside main */
if (fabs(a - b) < 1e-9) {
    printf("Close enough\n");
}

fabs geeft een absoluut verschil terug. Kies de tolerantie passend bij de schaal en precisie van de waarden; 1e-9 is een voorbeeld, geen universele instelling. Voor sommige Linux-builds is -lm aan het einde van de compileropdracht nodig om wiskundige functies te koppelen.

Precisie beïnvloedt grote gehele getallen

Een binaire double stelt elk geheel getal tot en met 2⁵³ exact voor. Een binaire float kan boven 2²⁴ = 16.777.216 niet meer elk geheel getal voorstellen: als je 16.777.217 aan zo’n float toewijst, krijg je 16.777.216. Een groter bereik betekent niet dat elke stap tussen gehele getallen beschikbaar blijft.

Bij het afdrukken wordt de opgeslagen binaire waarde afgerond. printf("%.2f\n", 2.675); drukt meestal 2.67 af, omdat de opgeslagen waarde iets kleiner is dan 2,675. Gebruik voor exacte bedragen in hele centen een geheel getal zoals long long cents = 1999; en maak de weergave aan het einde op.

Converteer en rond bewust af

Conversie naar een geheel getal kapt het breukdeel af naar nul: (int)3.99 is 3 en (int)-3.99 is −3. round uit <math.h> geeft een drijvendekommawaarde terug die is afgerond op het dichtstbijzijnde gehele getal, waarbij waarden halverwege van nul af worden afgerond: round(-2.5) geeft −3,0. Converteer dat resultaat alleen naar een geheel getal als het past. Een conversie van een drijvendekommawaarde naar een geheel getal waarvan het gehele deel buiten het bereik valt, heeft ongedefinieerd gedrag.

Bij IEEE-rekenen kunnen resultaten buiten het eindige bereik oneindig worden. Een drijvendekommawaarde anders dan nul delen door nul kan oneindig opleveren; nul gedeeld door nul levert NaN op, wat “geen getal” betekent. Een NaN is bij vergelijking zelfs ongelijk aan zichzelf. Dit zijn gedragingen van IEEE-drijvendekommagetallen; geheeltallige deling door nul heeft ongedefinieerd gedrag.

Belangrijkste punten

  • Drijvendekommatypen hebben een beperkte precisie en een beperkt bereik.
  • Vergelijk berekende meetwaarden met een geschikte tolerantie.
  • Bij het afdrukken wordt de opgeslagen benadering afgerond.
  • Conversie van een drijvendekommawaarde naar een geheel getal kapt af naar nul en vereist een representeerbaar resultaat.
3

Toon uitvoer en lees invoer met printf en scanf

Een programma heeft een duidelijke uitwisseling van tekst en waarden nodig. Maak uitvoer zo op dat een lezer labels, velden en afgeronde meetwaarden kan onderscheiden en stem elke invoerconversie af op de variabele die de waarde ontvangt. Het lezen van tekst kan verschillende resultaten opleveren: alle gevraagde waarden, slechts enkele waarden of helemaal geen conversie. Door die resultaten en de ongelezen tekst te controleren, zorg je dat latere berekeningen afhangen van waarden die het programma daadwerkelijk heeft verkregen.

3.1 Waarden met het bedoelde formaat afdrukken

printf zet waarden met een formaatstring om in tekst. Gewone tekens worden afgedrukt zoals ze zijn geschreven. Een conversie die met % begint, geeft aan hoe het volgende argument moet worden weergegeven.

Stem elke conversie af op het argument
printf("%d samples, %.2f kg\n", 4, 1.5);

Dit drukt 4 samples, 1.50 kg en een nieuweregelteken af. De conversies komen op volgorde overeen met de argumenten. Een formaat converteert het type van het opgegeven argument niet naar het type dat je bedoelde.

Waardetype of weergaveConversie
int%d of %i
unsigned int%u
long, long long%ld, %lld
size_t%zu
double, of een gepromoveerde float%f, %e, %g
long double%Lf
Teken, stringliteral%c, %s
Hexadecimaal zonder teken, octaal%x of %X, %o

Een float-argument wordt in printf gepromoveerd tot double, dus beide gebruiken %f; %lf is hier gelijkwaardig. Als je 3 doorgeeft aan %f, komen de typen niet overeen; het is geen verzoek om 3.0 af te drukken. Gebruik 3.0 of (double)3. Een niet-overeenkomend type of een ontbrekend argument kan ongedefinieerd gedrag veroorzaken. Bouw met waarschuwingen ingeschakeld, zodat formaatstrings die literals zijn worden gecontroleerd.

Stel breedte en precisie in

Een breedte is een minimaal aantal tekens, inclusief een teken en decimale punt. Een drijvendekomma-precisie na de punt bepaalt het aantal decimalen voor %f. In %8.2f is acht de minimale breedte en twee het aantal cijfers na de decimale punt. Een breder getal wordt volledig afgedrukt; de breedte kapt het niet af.

Met het gehele getal 42 geeft %5d de uitvoer 42, %-5d geeft 42 , %05d geeft 00042 en %+d geeft +42. Zonder precisie drukt %f zes decimalen af.

printf("%-6s%8s\n", "Item", "Price");
printf("%-6s%8.2f\n", "Tea", 2.5);
printf("%-6s%8.2f\n", "Cake", 12.75);

Elk label neemt een links uitgelijnd veld van zes tekens in, gevolgd door een rechts uitgelijnd prijsveld van acht tekens. Beide prijzen hebben twee decimalen, zodat de decimale punten onder elkaar staan. De spaties komen van de veldbreedten, niet van automatische scheiding tussen argumenten.

Wetenschappelijke en algemene weergave

%e gebruikt wetenschappelijke notatie: 12.345,678 wordt bij de standaardprecisie afgedrukt als 1.234568e+04. Voor %g telt de precisie significante cijfers, standaard zes. Het gebruikt wetenschappelijke notatie wanneer de decimale exponent kleiner is dan −4 of minstens gelijk is aan de precisie, en verwijdert nullen aan het einde. Zo drukt %g 0,0001 af als 0.0001, 0,00001 als 1e-05 en 1.000.000,0 als 1e+06. Het kiest niet simpelweg de kortst mogelijke schrijfwijze.

Druk speciale tekens af

In een C-string betekent \n een nieuweregelteken, \t een tab, \\ een backslash en \" een aanhalingsteken. Een procentteken in een formaat is %%: printf("100%%\n"); drukt 100% en een nieuweregelteken af.

printf voegt zelf geen spaties of regeleinden toe. printf("Count:"); printf("%d", 4); drukt Count:4 af. Een terminal kan tekst zonder nieuweregelteken vertraagd tonen; fflush(stdout); verzendt een wachtende prompt voordat om invoer wordt gevraagd.

Belangrijkste punten

  • Stem conversies af op de werkelijke typen van de argumenten.
  • De veldbreedte is een minimum; de precisie van %f telt decimalen.
  • De precisie van %g telt significante cijfers.
  • Schrijf %% voor een letterlijk procentteken in een formaat.
  • printf voegt geen spaties of nieuweregelteken toe tenzij het formaat daarin voorziet.

3.2 Invoer lezen en controleren wat is gelezen

scanf leest tekst, converteert die en slaat waarden op in variabelen. Controleer het resultaat voordat je die variabelen gebruikt. Tekst die met cijfers begint, kan slechts gedeeltelijk worden geaccepteerd, dus een geslaagde conversie en een volledig geldige invoerregel zijn verschillende dingen.

Geef een bestemming van het juiste type op
int count;
double mass_kg;
if (scanf("%d %lf", &count, &mass_kg) != 2) {
    return 1;
}

De & geeft het adres van elke variabele door zodat scanf daarin kan opslaan. %d vereist een int, %u een geheel getal zonder teken, %ld een long en %lld een long long. Voor drijvendekomma-invoer vereist %f een float en %lf een double. Anders dan bij printf zijn deze twee conversies niet uitwisselbaar. Een ontbrekende & of een verkeerd bestemmingstype kan ongedefinieerd gedrag veroorzaken.

%d leest decimaal. %i herkent ook hexadecimale invoer en octale invoer met een voorloopnul; met %i betekent de tekst “010” 8. Gebruik %d voor gewone decimale aantallen.

Controleer de retourwaarde

scanf geeft het aantal voltooide toewijzingen terug. Voor twee gevraagde waarden betekent succes 2. Een mislukte conversie laat haar bestemming ongewijzigd. Die kan nog niet-geïnitialiseerd zijn, dus gebruik de variabele niet tenzij het teruggegeven aantal bevestigt dat de conversie is geslaagd. EOF betekent dat de invoer eindigde of een invoerfout optrad voordat de eerste conversie was voltooid.

Bij scanf("%d:%d", &h, &m) moet de dubbele punt letterlijk voorkomen. De invoer “14:30” vult beide variabelen. De invoer “14 30” vult h, vindt daarna niet de vereiste dubbele punt en laat m ongewijzigd. De retourwaarde is 1.

Begrijp witruimte en gedeeltelijke invoer

Numerieke conversies slaan voorafgaande spaties, tabs en nieuweregeltekens over. %c leest het eerstvolgende teken, inclusief een achtergebleven nieuweregelteken. Gebruik scanf(" %c", &choice) om witruimte over te slaan voordat je een zichtbaar teken leest. Vermijd een afsluitend nieuweregelteken in het formaat: dat kan ervoor zorgen dat de aanroep wacht op een volgend teken dat geen witruimte is.

Met %d slaat “12abc” 12 op en laat “abc” ongelezen. “3.7” slaat 3 op en laat “.7” staan. “abc” slaat niets op en geeft 0 terug. Als je dezelfde conversie opnieuw aanroept zonder ongeldige invoer te verwerken, kan die eindeloos op hetzelfde teken blijven mislukken.

Herstel na een mislukte conversie
int n, result;
while ((result = scanf("%d", &n)) != 1) {
    if (result == EOF) {
        return 1;
    }
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
    }
    printf("Try again: ");
    fflush(stdout);
}

getchar geeft een int terug, zodat de functie zowel een teken als EOF kan voorstellen. De binnenste lus gooit de mislukte regel weg. De buitenste lus probeert het opnieuw, maar accepteert nog steeds een numeriek begin zoals 12 in “12abc”. Als je vereist dat het volgende teken '\n' is, controleer je de strengere regel van één geheel getal dat direct door een nieuweregelteken wordt gevolgd; die regel wijst ook afsluitende spaties af. Een beleid dat spaties of een laatste regel die bij EOF eindigt toestaat, moet die gevallen expliciet afhandelen.

Controleer na een geslaagde conversie het toegestane bereik, zoals 1 tot en met 8 voor een hoogte-instelling. scanf wijst een numerieke waarde buiten het bereik niet veilig af: een waarde die te groot is voor de bestemming heeft ongedefinieerd gedrag. Gebruik testinvoer die in het gekozen type past. Om een resultaat zoals massa gedeeld door lengte in het kwadraat te berekenen, controleer je of de meetwaarden positief zijn en houd je het kwadraat gegroepeerd als height_m * height_m.

Belangrijkste punten

  • Geef adressen van variabelen door en gebruik %lf om een double te lezen.
  • Vereis dat het teruggegeven aantal overeenkomt met het aantal benodigde waarden.
  • Een geslaagde conversie kan extra invoer ongelezen laten.
  • Verwerk ongeldige invoer voordat je een mislukte numerieke conversie opnieuw probeert.
  • Controleer na het lezen de toegestane waarden; invoerconversie heeft eigen grenzen voor het typebereik.
  • Gebruik " %c" als het lezen van een teken witruimte moet overslaan.
4

Reken met operatoren en expressies

Een expressie combineert waarden, typen en operatoren. Volg eerst geheeltallige deling en bijwerkingen en daarna de conversies die bij elke bewerking plaatsvinden. Vergelijkingen en logische controles bepalen of een berekening wordt bereikt, terwijl bitsgewijze bewerkingen op afzonderlijke bits werken. Onderscheid ten slotte de groepering door operatorprioriteit van de volgorde die nodig is wanneer een expressie een variabele wijzigt. Zowel de waarde als het type van een tussenresultaat is van belang.

4.1 Rekenen, toewijzing en wijzigingen met één stap

Elke C-expressie heeft een waarde en een type. Het type bepaalt hoe een bewerking werkt. Geheeltallige deling kan bijvoorbeeld geen resultaat met een breukdeel bewaren, ook niet als je het later in een drijvendekommavariabele opslaat.

Quotiënten en resten

+, - en * tellen op, trekken af en vermenigvuldigen. / deelt en % geeft een geheeltallige rest. Vermenigvuldiging, deling en rest binden sterker dan optelling en aftrekking.

Wanneer beide operanden van een deling gehele getallen zijn, kapt C het quotiënt af naar nul. 17 / 5 is 3, -7 / 2 is −3 en 7 / -3 is −2. De rest voldoet aan de delingsidentiteit: als je het quotiënt met de deler vermenigvuldigt en de rest optelt, krijg je het deeltal. Zo is -7 % 2 gelijk aan −1 en 7 % -3 aan 1. Een rest anders dan nul heeft het teken van het deeltal.

Gebruik n % 2 == 0 om te controleren of een getal even is. Gebruik n % 2 != 0 om bij positieve of negatieve gehele getallen te controleren of ze oneven zijn; een controle op precies 1 mist negatieve oneven waarden. % vereist gehele operanden; <math.h> biedt fmod voor drijvendekommaresten. Een geheeltallige deler nul maakt zowel / als % ongedefinieerd, dus controleer de deler vóór de bewerking.

Voor 135 minuten geeft 135 / 60 twee hele uren en 135 % 60 15 resterende minuten. Voor een positief decimaal geheel getal haalt % 10 het laatste cijfer op en verwijdert / 10 dat cijfer: 4096 geeft 6 en 409.

Behoud breukdelen wanneer je ze nodig hebt
int correct = 45, total = 60;
double percent = 100.0 * correct / total;

De drijvendekommaconstante maakt de vermenigvuldiging een drijvendekommabewerking en de deling geeft 75,0. In correct / total * 100 is de eerste bewerking een geheeltallige deling die nul geeft. Door die nul te vermenigvuldigen kun je het verloren breukdeel niet terugkrijgen. Eerst geheeltallig vermenigvuldigen, correct * 100 / total, geeft hier 75, mits het product past.

Sla een nieuwe waarde op

Een toewijzing evalueert de rechterkant en slaat de geconverteerde waarde links op. De toewijzingsexpressie heeft die opgeslagen waarde, dus a = b = 0; zet beide variabelen op nul en groepeert vanaf rechts.

Samengestelde toewijzingen combineren een berekening met opslag: x += 5 telt vijf op bij x en x /= 2 deelt x door twee met het huidige type. De hele rechteroperand hoort bij de berekening. Als x gelijk is aan 4, maakt x *= 2 + 1 die waarde 12, omdat de instructie met drie vermenigvuldigt.

Prefix- en postfixwijzigingen

++ telt één op en -- trekt één af. Op een eigen regel hebben i++; en ++i; hetzelfde uiteindelijke effect. Binnen een grotere expressie levert postfix de oude waarde op; prefix levert de nieuwe waarde op.

BeginInstructieEindwaarden
a = 5y = a++;y = 5, a = 6
a = 5y = ++a;y = 6, a = 6

Houd wijzigingen eenvoudig te ordenen. i = i++ + 1; en printf("%d %d\n", n, n++); bevatten tegenstrijdige, niet-geordende toepassingen van dezelfde variabele en hebben ongedefinieerd gedrag. Scheid het lezen, het bijwerken en de uitvoer in afzonderlijke instructies wanneer hun volgorde van belang is.

Belangrijkste punten

  • Geheeltallige deling kapt af naar nul.
  • Een geheeltallige rest anders dan nul heeft het teken van het deeltal.
  • Gebruik vóór de deling een drijvendekommaoperand als je een breukdeel nodig hebt.
  • Postfix levert de oude waarde op; prefix levert de bijgewerkte waarde op.
  • Zet tegenstrijdige leesacties en bijwerkingen in afzonderlijke instructies.

4.2 Typen worden bepaald voordat een resultaat wordt opgeslagen

C converteert waarden telkens wanneer een operator wordt toegepast. De doelvariabele ontvangt het voltooide resultaat; die grijpt niet terug in de expressie om eerdere bewerkingen te veranderen.

Converteer vóór het delen
int sum = 7, count = 2;
double a = sum / count;
double b = (double)sum / count;
double c = (double)(sum / count);

a wordt 3,0: de twee gehele operanden leveren het gehele getal 3 op, dat vervolgens wordt geconverteerd. b wordt 3,5: de expliciete typeconversie converteert sum vóór de deling, zodat ook count naar double wordt geconverteerd. c wordt 3,0: de typeconversie staat buiten de deling en komt nadat het breukdeel is weggegooid.

Dezelfde regel geldt binnen een langere expressie. 7 / 2 * 2.0 geeft 6,0, omdat het eerste resultaat het gehele getal 3 is. 7 / 2.0 * 2 geeft 7,0. Doorloop de operatoren in de volgorde van hun groepering en noteer het type van elke tussenwaarde.

Bepaal een gemeenschappelijk rekenkundig type

Als een van de operanden een drijvendekommawaarde is, converteert een rekenkundige bewerking doorgaans naar het breedste betrokken drijvendekommatype: long double, dan double, dan float. Bij gehele getallen ondergaan kleine typen eerst integerpromotie. Op platforms met 8-bits char, 16-bits short en 32-bits int past elke waarde van de kleinere typen in int, zodat die typen worden gepromoveerd tot int.

Met unsigned char a = 200, b = 100; is a + b het gehele getal 300 op een systeem met 8-bits bytes en 32-bits int. Tijdens de optelling loopt de waarde niet modulair terug bij 255. Als je 300 opslaat in een 8-bits tekentype zonder teken, wordt de waarde verlaagd tot 44. Promotie kan ook overloop bij getallen met teken veroorzaken: twee waarden van unsigned short van 50.000 worden als int vermenigvuldigd, maar 2.500.000.000 is groter dan een 32-bits INT_MAX.

Voor typen met en zonder teken bepalen rang en representeerbaar bereik het gemeenschappelijke type. int met unsigned int wordt geconverteerd naar een type zonder teken. Een type met teken en een hogere rang kan de bewerking met teken houden wanneer het elke waarde van het type zonder teken kan voorstellen; anders kan nog steeds rekenen zonder teken ontstaan.

De valkuil van typen met en zonder teken

Bij een 32-bits geheel getal zonder teken is -1 < 0u onwaar. De −1 met teken wordt vóór de vergelijking geconverteerd naar de waarde zonder teken 4.294.967.295. Evenzo geeft 2u - 10 de waarde 4.294.967.288. Een resultaat van sizeof is ook zonder teken, wat van belang is bij vergelijking met een mogelijk negatief aantal. Schakel waarschuwingen in en houd samenhangende grootheden in compatibele typen.

Bij opslagconversies kan informatie verloren gaan

Een conversie van een drijvendekommawaarde naar een geheel getal kapt af naar nul. Het gehele deel moet passen, anders is het gedrag ongedefinieerd. Een geheel getal dat naar een N-bits type zonder teken wordt geconverteerd, wordt gereduceerd modulo 2ᴺ; −1 wordt de maximale waarde zonder teken. Onder C17 geeft een geheeltallige conversie buiten het bereik naar een type met teken een door de implementatie bepaald resultaat of wekt die een door de implementatie bepaald signaal op. Die conversie verschilt van ongedefinieerde rekenkundige overloop bij getallen met teken.

Tekenconstanten nemen ook deel aan berekeningen. '7' - '0' geeft 7 omdat C opeenvolgende codes voor decimale cijfers garandeert. In ASCII zijn ook letters opeenvolgend, maar ga niet uit van die eigenschap voor elke tekenset. In formules met gehele getallen kan vermenigvuldigen vóór het delen alleen nuttige informatie behouden als dat product in het berekeningstype past.

Belangrijkste punten

  • Het doeltype verandert eerdere rekenkundige bewerkingen niet.
  • Converteer een operand expliciet vóór de bewerking die een breder type of een drijvendekommatype nodig heeft.
  • Kleine gehele getallen worden vóór rekenkundige bewerkingen gepromoveerd.
  • Een combinatie van operanden met en zonder teken kan vergelijkingen en aftrekkingen veranderen.
  • Voor conversie buiten het bereik en rekenkundige overloop bij getallen met teken gelden verschillende regels.
  • Volg zowel tussentypen als eindwaarden.

4.3 Voorwaarden en bitpatronen

Een voorwaarde vraagt of iets waar is. Een bitsgewijze bewerking werkt op de afzonderlijke bits van een geheel getal. C geeft deze taken verschillende operatoren, ook wanneer hun symbolen op elkaar lijken.

Vergelijk waarden en combineer controles

De vergelijkingen <, >, <=, >=, == en != leveren een int op: 1 voor waar en 0 voor onwaar. In een voorwaarde is nul onwaar en elke waarde anders dan nul waar. Zo test if (count) of count ongelijk aan nul is. == vergelijkt twee waarden; = wijst een nieuwe toe.

&& betekent dat beide voorwaarden waar moeten zijn, || betekent dat minstens één waar moet zijn en ! keert de waarheidswaarde om. Ze geven 0 of 1 terug. Voor een bereik van 1 tot en met 8 zijn twee vergelijkingen nodig:

height >= 1 && height <= 8

1 <= height <= 8 drukt dat bereik in C niet uit. Het wordt gegroepeerd als (1 <= height) <= 8, zodat de tweede vergelijking 0 of 1 ontvangt en altijd waar is. Om buiten het bereik te testen, schrijf je height < 1 || height > 8.

Bij het ontkennen van een samengestelde voorwaarde veranderen beide delen: !(a && b) is gelijkwaardig aan !a || !b en !(a || b) aan !a && !b. Dit zijn de wetten van De Morgan. Ze verklaren waarom buiten een interval zijn een “of” vereist, terwijl erin zijn een “en” vereist.

Zet een controle vóór de risicovolle bewerking
if (d != 0 && n / d > 2) {
    printf("Above two\n");
}

&& evalueert eerst de linkerkant. Als die onwaar is, wordt de rechterkant overgeslagen. || slaat de rechterkant over wanneer de linkerkant waar is. Met deze kortsluitevaluatie kan een controle een latere bewerking beschermen. Als je de twee delen hierboven omkeert, wordt gedeeld voordat de deler is gecontroleerd. Voor gehele getallen met teken bestaat een afzonderlijk bereikprobleem wanneer INT_MIN door −1 wordt gedeeld op een systeem met tweecomplement; controleer ook dat paar wanneer willekeurige waarden mogelijk zijn.

Een schrikkeljaarcontrole combineert deelbaarheid en uitzonderingen: (y % 4 == 0 && y % 100 != 0) || y % 400 == 0. Het jaar 2000 voldoet aan de controle op 400; 1900 niet, omdat de eeuwuitzondering geldt.

Werk bit voor bit

De bitsgewijze & behoudt bits die in beide operanden zijn gezet; | behoudt bits die in een van beide zijn gezet; ^ behoudt bits die verschillen; ~ keert bits om. Met 12 (1100) en 10 (1010) zijn de resultaten 8, 14 en 6 voor &, | en ^.

Bitverschuivingen verplaatsen bits. Voor niet-negatieve waarden waarbij het resultaat past, vermenigvuldigt x << k met 2ᵏ en gooit x >> k k lage bits weg, wat deelt door 2ᵏ met weggooien van de rest. Gebruik typen zonder teken voor bitsgewijze bewerkingen. Onder C17 is verschuiven met een negatief aantal of minstens de breedte van het gepromoveerde type ongedefinieerd; een negatieve waarde met teken naar links verschuiven is ongedefinieerd en die naar rechts verschuiven is door de implementatie bepaald.

Bitposities beginnen bij nul. flags | (1u << 3) zet bit 3; flags & ~(1u << 3) wist die; (flags >> 3) & 1u leest die. 1 & 2 is 0 omdat er geen gemeenschappelijke bit is, terwijl 1 && 2 gelijk is aan 1 omdat beide waarden ongelijk aan nul zijn. Bitsgewijze operatoren evalueren beide operanden en kunnen logische controles niet vervangen.

Belangrijkste punten

  • Vergelijkingen en logische operatoren leveren 0 of 1 op.
  • Een bereikcontrole verbindt twee vergelijkingen met &&.
  • Controles met kortsluitevaluatie moeten vóór de beschermde bewerking komen.
  • Gebruik logische operatoren voor waarheidswaarden en bitsgewijze operatoren zonder teken voor maskers.
  • Bitposities beginnen bij nul; het aantal verschuivingen moet binnen de breedte van het gepromoveerde type vallen.

4.4 Expressies groeperen en effecten ordenen

Twee vragen helpen je een expressie te lezen: welke operanden horen bij elke operator en wanneer vinden bewerkingen met neveneffecten plaats? Operatorprioriteit en associativiteit beantwoorden de vraag over groepering. Ze leggen doorgaans niet de evaluatievolgorde van operanden vast.

Volg de operatorprioriteit

Operatoren hoger in deze tabel binden sterker. Elke rij staat lager dan de voorgaande rij; de operatoren die door “dan” worden gescheiden, hebben afzonderlijke niveaus.

Van hoog naar laagOperatoren
Postfixx++, x--, aanroepen
Unair++x, --x, +, -, !, ~, expliciete typeconversies, sizeof, adresoperator &
Vermenigvuldiging*, /, %
Optelling+, -
Bitverschuiving<<, >>
Relationeel<, <=, >, >=
Gelijkheid==, !=
Bitsgewijs&, dan ^, dan `
Logisch&&, dan `
Voorwaardelijk?:
Toewijzing=, += en andere samengestelde toewijzingen
Komma,

Haakjes bepalen de groepering. 2 + 3 * 4 is 14; (2 + 3) * 4 is 20.

Gebruik associativiteit op gelijke niveaus

De meeste binaire rekenkundige operatoren groeperen van links naar rechts. 10 - 4 - 3 betekent (10 - 4) - 3, wat 3 is. 100 / 10 / 2 geeft 5. Toewijzing groepeert van rechts naar links, dus a = b = 0 slaat nul op in beide variabelen. Groepering beschrijft de structuur van de expressie; die legt geen algemene regel voor uitvoering van links naar rechts vast.

Volg voor int a = 3, b = 4, c = 5; de expressie a + b * c > 20 && c % 2 == 1. Vermenigvuldiging geeft 20, optelling geeft 23 en 23 > 20 geeft 1. De rest is 1 en de bijbehorende gelijkheidscontrole geeft 1. De laatste 1 && 1 is gelijk aan 1.

Bescherm bitcontroles en toewijzingen met haakjes

x & 1 == 0 betekent x & (1 == 0) en levert dus altijd nul op. Schrijf (x & 1) == 0 om de lage bit te testen. Optellen bindt ook sterker dan verschuiven: 1 << 2 + 1 is 1 << 3, oftewel 8.

Toewijzing bindt minder sterk dan vergelijking. a = b == c slaat de 0 of 1 van de vergelijking op in a. In while ((ch = getchar()) != '\n') zorgen de binnenste haakjes ervoor dat de toewijzing eerst het teken ontvangt. Zonder die haakjes zou ch een vergelijkingsresultaat ontvangen.

Kies één waarde of orden twee acties

condition ? first : second evalueert de voorwaarde en daarna alleen de gekozen waarde-expressie. max = (a > b) ? a : b; kiest een waarde; een if kan een blok met acties kiezen. De kommaoperator evalueert eerst zijn linkerexpressie en levert de waarde van zijn rechterexpressie op. Die kan twee bijwerkingen in de kop van een for-lus op elkaar afstemmen. Komma’s tussen functieargumenten zijn scheidingstekens en leggen die volgorde niet vast.

Houd neveneffecten gescheiden

C laat veel volgorden van operanden en argumenten ongespecificeerd. n++ * n++ heeft niet-geordende wijzigingen van dezelfde variabele en dus ongedefinieerd gedrag. Extra haakjes herstellen de ontbrekende ordening niet. Gebruik afzonderlijke instructies wanneer de ene actie aan de andere moet voorafgaan. &&, ||, ?: en de kommaoperator bieden specifieke regels voor de volgorde; gewone rekenkundige operatorprioriteit biedt zo’n garantie niet.

Belangrijkste punten

  • Operatorprioriteit bepaalt de groepering; die bepaalt doorgaans niet de evaluatievolgorde.
  • Op één rekenkundig niveau is de groepering meestal van links naar rechts.
  • Zet een bitsgewijs resultaat tussen haakjes voordat je het vergelijkt.
  • De voorwaardelijke operator evalueert alleen de gekozen waarde-expressie.
  • De kommaoperator ordent operanden; komma’s die argumenten scheiden doen dat niet.
  • Scheid tegenstrijdige bijwerkingen om ongedefinieerd gedrag te voorkomen.
5

Stuur het programmaverloop met voorwaarden en lussen

Het programmaverloop bepaalt welke instructies worden uitgevoerd en hoe vaak. Gebruik voorwaarden om een tak te kiezen, exacte gehele waarden om een ingang in een switch te kiezen en lussen om een taak te herhalen totdat aan de stopregel is voldaan. Uitvoeringssporen maken de volgorde zichtbaar: beginwaarden, controles, instructies in de lusinhoud en bijwerkingen. Let vooral op grenswaarden in de invoer en op de bestemming van break of continue binnen geneste constructies, waar een kleine wijziging in de syntaxis het hele verloop kan veranderen.

5.1 De juiste tak kiezen

Een if-instructie kiest of een actie wordt uitgevoerd. De voorwaarde moet tussen haakjes staan. Nul betekent onwaar; elke waarde anders dan nul betekent waar.

Gebruik accolades om de tak te tonen
if (temperature < 0) {
    printf("Freezing\n");
} else {
    printf("At or above freezing\n");
}

Het eerste blok wordt uitgevoerd bij een negatieve temperatuur. Bij precies nul is de vergelijking onwaar en wordt het else-blok uitgevoerd. Lees de grens rechtstreeks af uit <, <=, > of >=; de melding moet bij die grens passen.

Zonder accolades wordt alleen de volgende instructie door de voorwaarde gestuurd:

if (temperature > 30)
    printf("Hot\n");
printf("Drink water\n");

De tweede melding wordt bij elke temperatuur uitgevoerd. Inspringen helpt mensen, maar verandert de grammatica niet. Met accolades kun je een tweede instructie aan een tak toevoegen zonder die per ongeluk erbuiten te laten.

Orden een else-if-keten

Een if-else if-else-keten voert de eerste tak uit waarvan de voorwaarde waar is en slaat daarna de rest over. Begin bij scorecategorieën met de hoogste ondergrens:

if (score >= 90) {
    grade = 'A';
} else if (score >= 80) {
    grade = 'B';
} else if (score >= 70) {
    grade = 'C';
} else {
    grade = 'F';
}

Een score van 85 voldoet niet aan de eerste controle en wel aan de tweede. Een score van 90 voldoet aan de eerste. Als je eerst score >= 70 zou testen, zou 85 die tak ingaan en nooit de bedoelde B-tak bereiken. Afzonderlijke if-instructies gedragen zich anders: elke voorwaarde wordt onafhankelijk getest, zodat meerdere blokken kunnen worden uitgevoerd.

Let op twee kleine syntaxisfouten

if (x = 5) wijst 5 toe en behandelt die waarde anders dan nul vervolgens als waar. Gebruik if (x == 5) om te vergelijken. Bij if (x = 0) vindt de toewijzing nog steeds plaats, maar is de voorwaarde onwaar. Waarschuwingen helpen onbedoelde toewijzingen in voorwaarden zichtbaar te maken.

In if (x > 0); is de puntkomma een lege instructie die door de voorwaarde wordt gestuurd. Een blok dat erna staat, wordt onafhankelijk uitgevoerd, ook als x negatief is. Voeg geen puntkomma toe tussen een if-voorwaarde en het bedoelde blok.

Koppel elke else expliciet

Een else hoort bij de dichtstbijzijnde eerdere if die nog geen else heeft. Gebruik bij geneste keuzes accolades om de bedoelde koppeling duidelijk te maken. Nesten is handig wanneer controles die niet slagen verschillende meldingen vereisen: controleer eerst of een meetwaarde geldig is en controleer daarna of de waarde binnen de gewenste categorie valt. Beide combineren met && werkt wanneer maar één gezamenlijke actie nodig is.

Zet uitzonderingen vóór algemene regels

Schrikkeljaren bieden een nuttige keuze op volgorde. Controleer eerst de deelbaarheid door 400, wat een schrikkeljaar betekent. Controleer daarna de deelbaarheid door 100, wat geen schrikkeljaar betekent. Controleer vervolgens de deelbaarheid door 4; anders is het jaar geen schrikkeljaar. Voor 2000 geldt de eerste tak. Voor 1900 geldt de tweede. Als je alleen de deelbaarheid door vier zou controleren, zou je de eeuwuitzondering missen. De volgorde is van belang omdat algemene regels gevallen met een specifiekere uitzondering kunnen bevatten.

Belangrijkste punten

  • Een else-if-keten voert alleen de eerste tak uit waarvan de voorwaarde waar is.
  • Accolades bepalen welke instructies bij een tak horen.
  • == vergelijkt; = wijst toe.
  • Een puntkomma na een if-voorwaarde maakt een lege tak.

5.2 Vaste gevallen kiezen met switch

Een switch kiest een startpunt met één gehele waarde. Dit is handig voor een opdrachtteken of menunummer met verschillende vaste alternatieven. De uitvoering kan daarna doorlopen naar latere gevallen, tenzij je die stopt.

Kies een geval en verlaat het
switch (choice) {
    case 1:
        printf("Start\n");
        break;
    case 2:
        printf("Pause\n");
        break;
    default:
        printf("Unknown choice\n");
        break;
}

Als choice gelijk is aan 2, begint het programma bij case 2, drukt het Pause af en verlaat het de switch bij break. Als geen geval overeenkomt, begint de uitvoering bij default. Zonder default slaat een waarde zonder overeenkomst de switch over.

De sturende expressie moet een geheelgetaltype hebben, waaronder char of een enumeratie. Elk geval moet een afzonderlijke geheeltallige constante expressie zijn, zoals 3, 'q' of een geschikte #define-naam. In C17 is een const int-variabele geen geheeltallige constante expressie voor een case-label. Drijvendekommawaarden en strings kunnen geen sturende waarden van een switch zijn.

Begrijp doorvallen

Case-labels markeren ingangen, geen afzonderlijke blokken die automatisch eindigen. Verwijder de eerste break hierboven en een keuze van 1 drukt zowel Start als Pause af. Het programma test niet of choice gelijk is aan 2 voordat het door dat geval verdergaat. De uitvoering stopt pas bij een overdracht van de besturing of het einde van de switch.

Meerdere labels kunnen één actie delen:

case 'y':
case 'Y':
    printf("Yes\n");
    break;

Beide tekens leiden naar dezelfde actie. Wanneer een geval bewust werk uitvoert en doorgaat naar een ander geval, leg je de bedoeling uit met commentaar. C23 biedt ook [[fallthrough]];. Waarschuwingen kunnen een ontbrekende break helpen vinden, maar bij het bewust groeperen van labels hoef je de actie niet te herhalen.

Geef lokale variabelen van een geval een blok

Onder C17 kan een declaratie niet direct op een case-label volgen. Zet de instructies van dat geval tussen accolades:

case 3: {
    int delay = 5;
    printf("%d\n", delay);
    break;
}

Het blok houdt delay ook lokaal voor dat geval. C23 versoepelt de beperking op een label vóór een declaratie, maar accolades maken het geldigheidsgebied nog steeds duidelijk.

Kies tussen switch en if

Gebruik switch voor exacte gehele alternatieven. Gebruik if-else if voor ongelijkheden, voorwaarden met meerdere variabelen of drijvendekommameetwaarden. Scorecategorieën van gelijke breedte kunnen soms door geheeltallige deling naar exacte gevallen worden omgezet. Bij scores van 0 tot en met 100 geeft score / 10 de waarde 8 voor 85; de gevallen 9 en 10 kunnen de hoogste categorie delen. Controleer dat waardenbereik, zodat negatieve of te hoge scores niet zonder melding aan een categorie worden toegewezen.

Een switch binnen een lus

Een break verlaat de dichtstbijzijnde omsluitende switch of lus. Binnen een switch die zelf in een lus staat, beëindigt de break alleen de switch. Om de menulus te beëindigen, stel je een vlag in die door de voorwaarde wordt gecontroleerd, of keer je terug uit main wanneer het programma moet eindigen. Een continue in die switch geldt voor de omsluitende lus en gaat daar naar de volgende iteratie. Ga na op welke constructie elke overdracht van de besturing betrekking heeft voordat je bepaalt of het menu stopt.

Belangrijkste punten

  • Een switch kiest een beginlabel met één gehele waarde.
  • De uitvoering valt door tot break, een andere overdracht van de besturing of het einde.
  • Case-waarden moeten verschillende geheeltallige constante expressies zijn.
  • Gebruik in C17 een blok voor een declaratie die lokaal is voor een geval.
  • Een break in een switch beëindigt niet ook een omsluitende lus.

5.3 Herhalen totdat de gegevens aangeven dat je moet stoppen

Een while-lus test een voorwaarde vóór elke iteratie. Een do-while-lus test na de lusinhoud en wordt dus minstens eenmaal uitgevoerd. Kies de vorm die de controle plaatst waar het programma die nodig heeft.

Volg een berekening die door een voorwaarde wordt gestuurd
int n = 4096, sum = 0;
while (n > 0) {
    sum += n % 10;
    n /= 10;
}

n % 10 haalt een cijfer op en n /= 10 verwijdert het. De waarden na de iteraties zijn:

Iteratiesumn
16409
21540
3154
4190

De volgende voorwaarde is onwaar, dus de lus eindigt met een cijfersom van 19. Als n bij nul begint, wordt de lusinhoud nul keer uitgevoerd en blijft de som nul. Negatieve invoer slaat deze lus ook over, dus een programma dat die accepteert, heeft een vastgelegde aanpak nodig.

Een tellende lus stelt een teller in, test die en werkt die bij. Andere lussen maken voortgang door gegevens te lezen of break te bereiken. Bij herhaald geheeltallig halveren van 100 zijn de waarden 50, 25, 12, 6, 3 en 1: er zijn zes iteraties nodig om één te bereiken.

Laat invoer de herhaling sturen
int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
    sum += x;
    count++;
}
if (count != 0) {
    printf("%.2f\n", (double)sum / count);
}

Bij invoer “5 8 3 0” beëindigt de stopwaarde nul de lus en wordt die niet opgeteld. De som is 16, het aantal is 3 en het weergegeven gemiddelde is 5.33. De controle van het teruggegeven aantal beschermt het gebruik van x; de controle van het aantal beschermt de deling als de eerste waarde nul is. Deze berekening vereist ook invoer en totalen die in int passen.

Zonder controle op de stopwaarde stopt while (scanf("%d", &x) == 1) bij een mislukte numerieke conversie of het einde van de invoer. In een terminal geeft Ctrl+D normaal gesproken het einde van de invoer aan op Linux en macOS; op Windows gebruik je Ctrl+Z gevolgd door Enter.

Voer eenmaal uit vóór de controle
int height;
do {
    printf("Height (1-8): ");
    fflush(stdout);
    if (scanf("%d", &height) != 1) {
        return 1;
    }
} while (height < 1 || height > 8);

De prompt en het lezen vinden plaats vóór de bereikcontrole. De waarden nul en negen leiden tot herhaling; de waarden één en acht worden geaccepteerd. De puntkomma na while (...) is in deze vorm vereist. De conversiecontrole beëindigt dit voorbeeld bij niet-numerieke invoer; de bereikcontrole alleen kan een mislukte conversie niet afhandelen.

Maak voortgang op elk pad dat doorgaat

break verlaat de binnenste lus onmiddellijk. continue slaat de rest van de lusinhoud over en keert in een while-lus terug naar de voorwaarde. Als een bijwerking onder een continue staat, wordt die ook overgeslagen. Een lus kan dan eindeloos dezelfde waarde blijven testen.

Bepaal voordat je een lus uitvoert wat er verandert en waarom de stopvoorwaarde uiteindelijk wordt bereikt. Een bewuste while (1) kan stoppen via break; een ontbrekende bijwerking van een teller is een andere situatie. Ctrl+C stopt een programma dat in een terminal vastzit. Test de kleinste invoer en de waarde op de stopgrens om fouten met nul iteraties en één extra iteratie op te sporen.

Belangrijkste punten

  • while test eerst; do-while test na minstens één iteratie.
  • Een stopwaarde stopt de invoer zonder deel uit te maken van de gegevens.
  • Initialiseer accumulatoren buiten de lus.
  • Een pad dat doorgaat, moet voortgang maken richting de stopvoorwaarde.
  • Een continue in een while-lus kan een latere bijwerking overslaan.
  • Controleer of er nul gegevensitems zijn voordat je een gemiddelde berekent.

5.4 Iteraties tellen en lussen combineren

Een for-lus houdt een telpatroon in één kop bij elkaar. Geneste lussen herhalen een binnenste taak voor elke buitenste iteratie. Beide zijn gemakkelijker te controleren als je de werkelijke tellerwaarden opsomt en elke uitgang aanwijst.

Volg de kop op volgorde
for (int i = 0; i < 3; i++) {
    printf("%d ", i);
}

De initialisatie wordt eenmaal uitgevoerd. Daarna wordt de voorwaarde getest, de lusinhoud uitgevoerd als die waar is en de bijwerking uitgevoerd vóór de volgende controle. De uitvoer is 0 1 2 . De variabele die in de kop is gedeclareerd, is lokaal voor de lus; declareer die vóór de lus als je de eindwaarde daarna nodig hebt.

Voor een niet-negatieve n wordt i = 0; i < n; i++ n keer uitgevoerd, met waarden van nul tot en met n − 1. Met i <= n wordt de lus één keer extra uitgevoerd. Voor een positieve stapgrootte s van a tot en met b is het aantal (b - a) / s + 1 met geheeltallige deling, mits de berekening en bijwerkingen van de teller passen. Van 2 tot en met 20 in stappen van 3 zijn de waarden 2, 5, 8, 11, 14, 17, 20: zeven iteraties.

Houd de accumulator buiten de lus
int total = 0;
for (int i = 1; i <= 100; i++) {
    total += i;
}

Het eindtotaal is 5050. De initialisatie moet eenmaal vóór de lus plaatsvinden. Als je int total = 0; naar de lusinhoud verplaatst, ontstaat bij elke iteratie een nieuw totaal en gaat het eerdere werk verloren.

Kies tellers die het einde kunnen bereiken

Een puntkomma direct na de kop van de for-lus maakt de lusinhoud leeg. Een daaropvolgend blok wordt dan eenmaal na de lus uitgevoerd. Gebruik accolades direct na de kop om de lusinhoud zichtbaar te maken.

Aftellen zonder teken met i >= 0 kan niet op die manier stoppen: waarden zonder teken zijn altijd niet-negatief en nul verlagen met één loopt modulair terug naar het maximum. Een drijvendekommateller die herhaaldelijk 0.1 optelt, kan de exacte waarde 1.0 overslaan, zodat x != 1.0 onbetrouwbaar is als stopregel. Tel met een geheel getal en bereken een meetwaarde zoals x = i / 10.0 binnen de lusinhoud.

Bouw rijen met geneste lussen

Voor elke buitenste iteratie begint de binnenste lus en doorloopt die haar eigen reeks. Drie buitenste iteraties en vier vaste binnenste iteraties voeren de binnenste lusinhoud twaalf keer uit. Wanneer de binnengrens afhangt van de rij, tel je elke rij afzonderlijk.

int height = 3;
for (int row = 1; row <= height; row++) {
    for (int s = 0; s < height - row; s++) printf(" ");
    for (int h = 0; h < row; h++) printf("#");
    printf("\n");
}

De rijen bevatten twee spaties en één hekje, één spatie en twee hekjes, en daarna drie hekjes. Het nieuweregelteken hoort na de binnenste lussen, zodat elke buitenste iteratie één rij maakt.

Sla werk over of stop voortijdig

continue in een for-lus gaat naar de bijwerking en daarna naar de volgende controle. Een lus die alleen oneven waarden van 1 tot en met 9 optelt, kan even waarden overslaan en eindigen met 25. break verlaat de binnenste lus onmiddellijk. In geneste lussen stopt een break in de binnenste lus de buitenste lus niet.

Een for-lus is vaak de duidelijkste keuze voor een bekend tellerbereik; while is geschikt voor herhaling die door gegevens wordt gestuurd en do-while voor werk dat vóór de controle moet gebeuren. Als je een for-lus herschrijft als een while-lus, moet je extra op continue letten, omdat de bijwerking ook op dat pad moet plaatsvinden.

Belangrijkste punten

  • Een for-lus voert de initialisatie eenmaal uit en de bijwerking na elke iteratie die doorgaat.
  • Controleer de doorlopen waarden om één-ernaastfouten op te sporen.
  • Gebruik gehele tellers met voldoende bereik.
  • Bij geneste lussen begint de binnenste reeks voor elke buitenste iteratie opnieuw.
  • Een break in de binnenste lus verlaat alleen die lus.
  • Een continue in een for-lus voert de bijwerking in de kop nog steeds uit.
  • Houd de initialisatie van een lopend totaal buiten de herhaalde lusinhoud.
6

Herken en voorkom overloop bij gehele getallen

Eindige bereiken van gehele getallen beïnvloeden berekeningen, ook wanneer het eindantwoord klein genoeg lijkt. Onderscheid modulaire terugloop bij getallen zonder teken, overloop bij getallen met teken en waarden die bij conversie verloren gaan. Onderzoek daarna de typen van tussenliggende bewerkingen. Preventie begint vóór een risicovolle berekening: verbreed een operand, bewijs de grenzen of gebruik een bewerking met overloopcontrole. Tellerbereiken en grenswaardetests helpen fouten te vinden die gewone kleine invoer en korte proefuitvoeringen kunnen missen.

6.1 Wat gebeurt er als een geheel getal niet past

Het bereik van een geheelgetaltype is eindig. Een wiskundig antwoord kan geldig zijn en toch te groot voor het C-type waarmee het wordt berekend. De gevolgen hangen af van de vraag of de bewerking zonder teken, met teken of een conversie is.

Rekenen zonder teken loopt modulair terug

Een N-bits resultaat zonder teken wordt gereduceerd modulo 2ᴺ. Met een 32-bits unsigned int geeft UINT_MAX + 1u nul, 0u - 1u geeft 4.294.967.295 en 3u - 5u geeft 4.294.967.294. Het resultaat is gedefinieerd, maar kan voor de taak verkeerd zijn: vijf items aftrekken van een voorraad van drie hoort geen miljarden items op te leveren.

Voor uint8_t b = 255; slaat b = b + 1; nul op. Op een typisch systeem met 32-bits int vindt de optelling eerst plaats als int en levert die 256 op; conversie van die waarde terug naar een 8-bits type zonder teken levert nul op. Kleine opslag zonder teken betekent niet dat elke tussenliggende bewerking dat kleine type heeft.

Rekenkundige overloop bij getallen met teken is ongedefinieerd

Bij een 32-bits int vereist één optellen bij INT_MAX de waarde 2.147.483.648. C definieert het rekenkundige resultaat met teken niet. Hardware kan modulair lijken terug te lopen naar INT_MIN, maar de compiler kan optimaliseren op basis van de aanname dat overloop bij getallen met teken nooit optreedt. Een controle zoals if (x + 1 < x) kan voor gehele getallen met teken worden verwijderd, omdat elke gedefinieerde optelling van één bij een getal met teken een grotere waarde oplevert.

Bereken niet eerst om daarna de schade te bekijken. Een ongedefinieerde bewerking heeft al plaatsgevonden voordat een latere controle wordt uitgevoerd.

Een breder doeltype kan te laat komen
int a = 50000, b = 50000;
long long product = a * b;

De vermenigvuldiging wordt uitgevoerd als int en loopt dus over op een systeem met 32-bits int. De breedte van het doeltype kan dat niet herstellen. (long long)a * b verandert het type van de bewerking vóór de vermenigvuldiging en levert 2.500.000.000 op.

Faculteiten groeien snel: 12! is 479.001.600 en past in een 32-bits geheel getal met teken; 13! is 6.227.020.800 en past niet. Bij 32-bits rekenen zonder teken loopt 13! modulair terug naar 1.932.053.504. Een 64-bits long long met teken bevat 20! = 2.432.902.008.176.640.000, maar niet 21!.

Voor conversie gelden afzonderlijke regels

Bij conversie van een geheel getal naar een type zonder teken wordt modulo gereduceerd. Onder C17 geeft conversie van een geheel getal buiten het bereik naar een type met teken een door de implementatie bepaald resultaat of signaal. Conversie van een drijvendekommawaarde waarvan het afgekaptе gehele deel niet in het gehele doeltype past, heeft ongedefinieerd gedrag. Een getal buiten het bereik lezen met een numerieke scanf-conversie is ook ongedefinieerd.

Bereikfouten kunnen grote gevolgen hebben

De eerste vlucht van Ariane 5 op 4 juni 1996 ging verloren nadat een drijvendekommawaarde niet in een 16-bits geheel getal met teken in de traagheidsreferentiesoftware paste. Dat programma was geschreven in Ada; de les over het controleren van conversiebereiken geldt ook voor C.

Een 32-bits secondeteller met teken die begint bij het Unix-tijdperk bereikt 2.147.483.647 op 19 januari 2038 om 03.14.07 uur UTC. Systemen die die tijdrepresentatie gebruiken, hebben een bredere representatie nodig. In 2015 pakte de FAA ook software voor de generatorbesturing van de Boeing 787 aan die na 248 dagen ononderbroken stroom kon overlopen. Deze fouten laten zien waarom zowel een lange bedrijfstijd als tussenwaarden in een schatting van het bereik thuishoren.

Drijvendekommagrenzen zijn anders: gangbare IEEE-systemen kunnen oneindig opleveren en een float kan precisie voor gehele getallen verliezen voordat het maximum wordt bereikt. Modulaire terugloop van gehele getallen zonder teken levert een gewone numerieke waarde op; overloop bij getallen met teken biedt helemaal geen betrouwbaar resultaat.

Belangrijkste punten

  • Rekenen zonder teken gebruikt reductie modulo.
  • Rekenkundige overloop bij getallen met teken heeft ongedefinieerd gedrag.
  • Een breder doeltype kan een tussenliggende bewerking in een smaller type niet herstellen.
  • Voor geheeltallige conversie, drijvendekommaconversie en rekenkundige overloop gelden verschillende regels.
  • Schat waarden over de volledige bedrijfsduur.

6.2 Overloop voorkomen voordat die optreedt

Begin met de grootste waarde die elke berekening kan opleveren. Neem tussenproducten en bijwerkingen van tellers mee, niet alleen het eindantwoord. Kies vervolgens een type of een voorafgaande controle die elke bewerking binnen het bereik houdt.

Verbreed de berekening vroeg

Een 32-bits geheel getal met teken bevat waarden tot ongeveer 2,1 miljard, terwijl een 64-bits long long met teken tot 9.223.372.036.854.775.807 gaat. Een millisecondenteller bereikt de 32-bits grens met teken in ongeveer 24,9 dagen. Gebruik een 64-bits type wanneer een tijdsduur langer kan zijn.

Schrijf (long long)a * b om een geheeltallig product te verbreden voordat het wordt geëvalueerd. Ook constanten zijn van belang: 86400LL * days begint de vermenigvuldiging als long long. (long long)(a * b) converteert pas na het product in het smallere type en kan dus overloop van dat product niet voorkomen. Bredere typen hebben nog steeds grenzen; controleer de grootst mogelijke waarden daartegen.

Controleer optelling met aftrekking

Neem voor int a en int b <limits.h> op en test:

if ((b > 0 && a > INT_MAX - b) ||
    (b < 0 && a < INT_MIN - b)) {
    printf("Sum out of range\n");
} else {
    int sum = a + b;
    printf("%d\n", sum);
}

Elke aftrekking wordt beschermd door het teken van b, zodat de controle zelf representeerbaar blijft. Voor 2.000.000.000 plus 200.000.000 is INT_MAX - b gelijk aan 1.947.483.647, lager dan a, dus wordt de optelling geweigerd. Het geval van gelijkheid past: wanneer a gelijk is aan INT_MAX - b, is de som precies INT_MAX.

Controleer positieve vermenigvuldiging door te delen

Voor positieve int-operanden betekent a > INT_MAX / b dat het product het maximum zou overschrijden. De voorwaarde voorkomt dat het product wordt berekend en voorkomt een deler nul omdat b positief is. Bij een faculteitsberekening detecteert de controle vóór vermenigvuldiging van 12! met 13 dat 479.001.600 groter is dan 165.191.049. Stop vóór die vermenigvuldiging.

Controleer bij een voorraad zonder teken vóór het aftrekken stock >= requested. Alleen overschakelen op een type zonder teken voorkomt geen onzinnig modulair teruggelopen resultaat.

Herschrijf risicovolle tussenexpressies

Het midden (low + high) / 2 kan overlopen, ook als het antwoord past. Wanneer 0 <= low <= high en beide int zijn, gebruik je low + (high - low) / 2. Het verschil is niet-negatief en binnen bereik en het resultaat is nooit groter dan high. Deze voorwaarden zijn van belang; de formule is geen algemeen bewijs voor willekeurige eindpunten met teken.

Ook lustellers hebben bereik nodig. Een 16-bits short kan 40.000 niet bereiken, dus een lus die daarmee zover telt, kan niet volgens de bedoeling eindigen. Gebruik een tellertype dat de grens en elke laatste bijwerking kan voorstellen.

Gebruik rekenen met overloopcontrole en tests

C23 biedt <stdckdint.h> met ckd_add, ckd_sub en ckd_mul. Zo slaat ckd_add(&r, a, b) een resultaat op en geeft de functie waar terug wanneer het wiskundige antwoord niet in r past. GCC en clang bieden ook __builtin_add_overflow, __builtin_sub_overflow en __builtin_mul_overflow als compileruitbreidingen.

Waarschuwingen vangen sommige gevallen van overloop in constanten en fouten met waarden met en zonder teken op. In GCC of clang kan -fsanitize=undefined -g overloop bij getallen met teken melden die tijdens een test wordt bereikt; -ftrapv vraagt om onderbrekingen bij overloop van optelling, aftrekking en vermenigvuldiging met teken. Test nul, één, min één, de grootste verwachte invoer en waarden op en vlak onder grenzen. Een testhulpmiddel controleert de paden die je uitvoert, dus blijf in de code over het bereik redeneren.

Belangrijkste punten

  • Verbreed een operand vóór een risicovolle bewerking.
  • Gebruik voorafgaande controles die rekening houden met het teken bij optelling van getallen met teken.
  • De voorafgaande controle door deling voor vermenigvuldiging vereist positieve operanden.
  • Ook aftrekken zonder teken vereist een bereikcontrole.
  • De veiligere middenformule vereist geordende niet-negatieve eindpunten.
  • Rekenen met overloopcontrole meldt of het resultaat in het doel past.
  • Grenswaardetests en sanitizers vullen redeneringen over het bereik aan.

Typen en operatoren in C: voorbeelden

6 Walkthroughs

1

Bouw een begroeting en onderzoek een mislukte wijziging

9 stappen
Volg een eerste programma van broncode tot uitvoering en gebruik daarna diagnostische meldingen en een uitvoeringsspoor om problemen te herstellen.
  1. 1
    IntroductieVolg het bouwproces

    Een opgeslagen C-programma is tekst. Om het uit te voeren is een uitvoerbaar bestand nodig dat uit die tekst is gemaakt. Houd de rollen van de twee bestanden duidelijk: een bewerking verandert de broncode, terwijl een geslaagd bouwproces het uitvoerbare bestand vervangt.

    Je bouwt een korte begroeting, bekijkt wat elke fase doet en onderzoekt een mislukte wijziging. De centrale werkwijze is het bouwresultaat controleren voordat je een uitvoering als bewijs voor de nieuwe broncode gebruikt.

  2. 2
    UitlegLees de opbouw van het programma

    Het programma begint bij main:

    #include <stdio.h>
    int main(void)
    {
        printf("Ready\n");
        return 0;
    }
    

    De header levert de declaratie van printf. De functie-inhoud drukt een regel af en geeft daarna succes terug. void betekent hier dat main geen parameters heeft; int noemt het type van de retourwaarde. Accolades omsluiten de functie-inhoud en eenvoudige instructies eindigen met puntkomma’s.

  3. 3
    VoorbeeldBouw het programma en voer het uit

    Sla de broncode op als ready.c. Voer in de map van het bestand in:

    gcc -std=c17 -Wall -Wextra ready.c -o ready
    ./ready
    

    Het bouwproces kiest C17 en nuttige waarschuwingen en geeft het uitvoerbare bestand de naam ready. De uitvoering drukt Ready af, gevolgd door een nieuweregelteken. Op Linux of macOS kiest ./ de huidige map. Je kunt op dezelfde plaats in de opdracht clang gebruiken; uitvoerbare bestanden op Windows gebruiken het achtervoegsel .exe.

  4. 4
    VerbandBekijk de rol van objectcode

    De voorbewerking verwerkt de include-richtlijn. Daarna levert compilatie assembleertaal op en levert assemblage objectcode op. Koppeling lost verwijzingen op en voltooit het uitvoerbare bestand; bibliotheekcode kan tijdens de uitvoering door een gedeelde bibliotheek worden geleverd.

    De opties -E, -S en -c stoppen in steeds latere fasen. In het bijzonder stopt -c bij een objectbestand: de koppelingsstap die nodig is voor het uitvoerbare bestand is dan nog niet voltooid.

  5. 5
    UitdagingVoorspel de volgende uitvoering

    Je bouwt de begroeting en slaat daarna een wijziging op die “Ready” in “Begin” verandert. Je voert het bestaande uitvoerbare bestand uit zonder te compileren. Welke tekst verschijnt en hoe verander je die?

    ---reveal---

    Het programma drukt nog steeds Ready af. Het uitvoerbare bestand weerspiegelt het eerdere geslaagde bouwproces. Compileer de opgeslagen wijziging en voer het nieuwe uitvoerbare bestand uit om Begin af te drukken. Alleen de broncode opslaan werkt de machinecode niet bij.

  6. 6
    VoorbeeldHerstel de eerste fout

    Door een wijziging ontbreekt de puntkomma op deze regel:

    printf("Begin\n")
    return 0;
    

    De compiler kan naar de eerste regel wijzen of naar return, waar de compiler het ontbrekende einde van de expressie-instructie opmerkt. Voeg de puntkomma na de aanroep toe en bouw opnieuw. Begin met deze eerste oorzaak, ook als latere meldingen andere regels noemen; één syntaxisfout kan de manier verstoren waarop de compiler de daaropvolgende code leest.

  7. 7
    TipGebruik waarschuwingen als nuttige aanwijzingen

    Een waarschuwing laat het bouwproces meestal doorgaan, maar kan op een ernstige fout wijzen. printf("%d\n", 2.5); geeft een double door waar het formaat int vereist, wat ongedefinieerd gedrag is.

    Herstel het verschil tussen de typen in plaats van te accepteren wat een proefuitvoering toevallig afdrukt. Waarschuwingen van -Wall -Wextra helpen zulke fouten zichtbaar te maken; -Werror laat die het bouwproces stoppen. Druk bij een verkeerde berekening tussenwaarden af en vergelijk die met een klein uitvoeringsspoor dat je met de hand maakt.

  8. 8
    UitdagingOnderscheid een mislukt bouwproces van een verkeerde uitvoering

    Na een geslaagd bouwproces voor de begroeting compileert een nieuwe wijziging niet. Als je ./ready uitvoert, wordt nog steeds een begroeting afgedrukt. Laat dat zien dat de wijziging geldig is en wat moet je vervolgens bekijken?

    ---reveal---

    Nee. Het oude uitvoerbare bestand kan na een mislukt bouwproces blijven staan. Bekijk de eerste compilatiefout, inclusief de voorafgaande bronregel, herstel die en bouw opnieuw totdat het lukt. Voer daarna het nieuwe uitvoerbare bestand uit en vergelijk de uitvoer met je voorspelling. De bestaande begroeting zegt niets over de mislukte wijziging.

  9. 9
    SamenvattingGebruik één duidelijke herstelcyclus

    Houd elke test verbonden met de broncode waaruit daadwerkelijk is gebouwd. Sla de wijziging op, compileer, lees de eerste diagnostische melding, herstel de oorzaak en bouw opnieuw voordat je het programma uitvoert.

    Als het programma wordt gebouwd maar de verkeerde waarde berekent, gebruik dan een klein uitvoeringsspoor en grenswaarden als invoer. Als een bewerking ongedefinieerd gedrag heeft, wijzig de code dan zodat dat gedrag niet kan optreden; normaal ogende uitvoer is geen betrouwbaar resultaat voor die bewerking.

2

Kies opslag voor aantallen en meetwaarden

13 stappen
Volg toewijzing en geldigheidsgebied en verbind daarna het bereik van gehele getallen en de drijvendekommaprecisie met praktische typekeuzes.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVraag wat een waarde nodig heeft
  2. 2
    VoorbeeldEen gekopieerde waarde blijft een kopie
  3. 3
    UitlegHoud namen binnen hun blokken
  4. 4
    UitdagingVolg een verborgen naam
  5. 5
    UitlegVergelijk bereiken voordat je kiest
  6. 6
    VoorbeeldLees een bitpatroon en een literal
  7. 7
    UitdagingKies een overdraagbaar bereik
  8. 8
    UitlegBekijk waarom decimale breuken afwijken
  9. 9
    VoorbeeldVergelijk met een toegestane fout
  10. 10
    UitdagingOnderscheid conversie van afronding
  11. 11
    KernpuntPrecisie kan eerder tekortschieten dan bereik
  12. 12
    UitdagingStem het type af op de taak
  13. 13
    SamenvattingControleer opslag op twee manieren
3

Lees meetwaarden en druk duidelijke resultaten af

11 stappen
Stem formaten af op typen, onderzoek gedeeltelijke invoer en volg een gecontroleerde berekening met meetwaarden.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVerbind getypeerde waarden met tekst
  2. 2
    UitlegLees het uitvoerformaat
  3. 3
    VoorbeeldMaak een uitgelijnde weergave
  4. 4
    UitdagingVoorspel een aangevuld geheel getal
  5. 5
    UitlegLees in de juiste bestemmingen
  6. 6
    VoorbeeldHerken een gedeeltelijke conversie
  7. 7
    TipLees niet per ongeluk een achtergebleven nieuweregelteken
  8. 8
    UitdagingVolg wat een mislukte leesactie behoudt
  9. 9
    VoorbeeldControleer het domein van de berekening
  10. 10
    UitdagingCombineer lezen en weergeven
  11. 11
    SamenvattingGebruik twee controles bij invoer
4

Lees een expressie van typen tot effecten

17 stappen
Doorloop deling, conversies, logische controles, maskers, groepering en bijwerkingen in een bepaalde volgorde.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieNoteer zowel waarden als typen
  2. 2
    VoorbeeldSplits een hoeveelheid in hele delen
  3. 3
    UitlegOnderscheid de twee incrementaties
  4. 4
    UitdagingVolg een samengestelde bijwerking
  5. 5
    VoorbeeldPlaats de drijvendekommaoperand vroeg genoeg
  6. 6
    KernpuntKleine waarden zonder teken kunnen als int met teken worden berekend
  7. 7
    UitdagingVolg een waarde met teken naar rekenen zonder teken
  8. 8
    UitlegVerbind volledige vergelijkingen
  9. 9
    VoorbeeldLaat een controle eerst plaatsvinden
  10. 10
    VoorbeeldVerander één vlag zonder andere te verstoren
  11. 11
    UitdagingVergelijk waarheidswaarden met gemeenschappelijke bits
  12. 12
    UitlegGroepeer voordat je het verloop volgt
  13. 13
    TipPlaats het masker binnen de vergelijking
  14. 14
    VerbandKies een waarde zonder beide te evalueren
  15. 15
    UitdagingKunnen haakjes twee bijwerkingen herstellen?
  16. 16
    UitdagingCombineer groepering met een bereikcontrole
  17. 17
    SamenvattingDoorloop een expressie in drie stappen
5

Volg keuzes en bouw een lopend totaal op

18 stappen
Doorloop takken, een switch-menu, invoer met een stopwaarde en genest tellen met bewuste uitgangen.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVolg het daadwerkelijk gekozen pad
  2. 2
    UitlegGebruik de eerste tak met een ware voorwaarde
  3. 3
    TipZowel accolades als gelijkheid zijn van belang
  4. 4
    UitdagingZoek de eerste passende categorie
  5. 5
    UitlegKies een ingang voor een opdracht
  6. 6
    VoorbeeldDeel bewust een actie
  7. 7
    UitdagingBepaal wat break verlaat
  8. 8
    UitlegLees invoer voordat je die gebruikt
  9. 9
    VoorbeeldVolg de berekening met de stopwaarde
  10. 10
    UitdagingHandel een onmiddellijke stop af
  11. 11
    VerbandVraag eenmaal met do-while
  12. 12
    UitlegTel precies het bedoelde aantal iteraties
  13. 13
    VoorbeeldBehoud een totaal tussen iteraties
  14. 14
    TipVermijd tellers die de uitgang missen
  15. 15
    VoorbeeldTel rijen afzonderlijk
  16. 16
    UitdagingVolg een voortijdige uitgang in geneste lussen
  17. 17
    UitdagingCombineer een vlag met een menulus
  18. 18
    SamenvattingControleer elke route naar de volgende iteratie
6

Maak een product veilig voordat je het berekent

12 stappen
Onderscheid regels voor overloop, verbreed op het juiste moment en controleer grensgevallen zonder de fout te veroorzaken die je test.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieOnderzoek de berekening vóór de opslag
  2. 2
    VoorbeeldZoek de bewerking met het smalle type
  3. 3
    UitlegRekenen zonder teken heeft een gedefinieerd maar mogelijk verkeerd resultaat
  4. 4
    UitdagingHerstelt een late typeconversie het product?
  5. 5
    VoorbeeldVerbreed de bewerking zelf
  6. 6
    UitlegControleer optelling zonder op te tellen
  7. 7
    VoorbeeldControleer een positief product door te delen
  8. 8
    UitdagingControleer de grens correct
  9. 9
    KernpuntEen veilig antwoord kan een onveilige som verbergen
  10. 10
    TipGebruik hulpmiddelen en test de grenzen
  11. 11
    UitdagingControleer een volledig ontwerp voor het bereik
  12. 12
    SamenvattingLever het bewijs vóór de bewerking

Typen en operatoren in C: oefenvragen

50 Oefenvragen in 5 sets

Set 1: Bouw programma’s en volg opgeslagen waarden10 Oefenvragen

Vraag 1 van 10

Een programma is met succes gebouwd en daarna is de begroeting gewijzigd en opgeslagen. Het bestaande uitvoerbare bestand drukt bij uitvoering nog steeds de oude begroeting af. Welke actie zorgt ervoor dat de wijziging wordt toegepast?
  1. Voer hetzelfde uitvoerbare bestand opnieuw uit.
  2. Verander de uitvoernaam van het uitvoerbare bestand.
  3. Compileer de opgeslagen broncode opnieuw.
  4. Voeg nog een nieuweregelteken toe aan de begroeting.
Toon antwoordVerberg antwoord
Antwoord: C
Het uitvoerbare bestand bevat het resultaat van het eerdere bouwproces. Broncode opslaan bouwt dat bestand niet opnieuw, dus compileer de gewijzigde broncode en voer daarna het nieuwe uitvoerbare bestand uit. Als je het oude uitvoerbare bestand opnieuw uitvoert, gebruikt het dezelfde eerdere machinecode en kan het de gewijzigde begroeting niet overnemen.

Vraag 2 van 10

Een compiler meldt een ontbrekende puntkomma, gevolgd door verschillende fouten op latere regels. Wat moet je eerst herstellen?
  1. Het eerst gemelde syntaxisprobleem.
  2. Het laatst gemelde syntaxisprobleem.
  3. Elke latere regel vóór het opnieuw bouwen.
  4. De invoer voor het uitvoerbare bestand.
Toon antwoordVerberg antwoord
Antwoord: A
Begin met de eerste fout en bekijk de genoemde regel en de vorige regel. Een ontbrekende puntkomma kan de interpretatie van latere instructies door de compiler verstoren. Als je eerst alle latere regels herstelt, kun je correcte code veranderen die alleen door de eerdere fout werd gemeld.

Vraag 3 van 10

Welke waarden worden door deze code afgedrukt?

int units = 6;
int total = units * 4;
units = 9;
printf("%d %d\n", units, total);
  1. 9 36
  2. 6 24
  3. 6 36
  4. 9 24
Toon antwoordVerberg antwoord
Antwoord: D
De declaratie van total berekent eenmaal zes maal vier en slaat 24 op. De latere toewijzing verandert units in negen maar berekent total niet opnieuw, dus de uitvoer is 9 en 24. Als je 36 verwacht, behandel je de eerdere toewijzing als een formule die de twee variabelen blijvend verbindt.

Vraag 4 van 10

Een waarde moet negatieve resultaten en positieve aantallen tot 6.000.000.000 kunnen bevatten op zowel gangbare 64-bits Linux- als Windows-systemen. Welk standaardgeheelgetaltype biedt het benodigde bereik?
  1. int
  2. long long
  3. unsigned int
  4. long
Toon antwoordVerberg antwoord
Antwoord: B
Een standaard-long long heeft minstens 64 bits en kan deze positieve en negatieve waarden voorstellen. Een gebruikelijke Windows-long is slechts 32 bits, ook wanneer Windows zelf 64-bits is. Unsigned int heeft op deze systemen ook onvoldoende bereik en kan geen negatieve waarden voorstellen.

Vraag 5 van 10

Welke decimale waarde stelt de C17-geheeltallige literal 014 voor?
  1. 14
  2. 10
  3. 12
  4. 16
Toon antwoordVerberg antwoord
Antwoord: C
Door de voorloopnul is 014 een octale literal. De cijfers stellen één groep van acht plus vier voor, wat twaalf oplevert. Als je dit leest als het decimale getal veertien, negeer je het voorvoegsel voor het grondtal; een decimale literal voor veertien schrijf je als 14 zonder voorloopnul.

Vraag 6 van 10

Een berekende double verschilt na enkele decimale bewerkingen zeer weinig van een beoogde double. Welke vergelijking past bij een meting die een kleine absolute fout toestaat?
  1. Vergelijk het absolute verschil met de toegestane fout.
  2. Vergelijk de afgedrukte waarden bij maximale precisie.
  3. Converteer beide waarden naar int en vergelijk ze.
  4. Wijs beide waarden toe aan double en vergelijk ze exact.
Toon antwoordVerberg antwoord
Antwoord: A
Een controle van het absolute verschil zoals fabs(actual - target) < tolerance controleert de fout die de meting toestaat. Exacte gelijkheid kan uitblijven doordat decimale berekeningen binaire afrondingsverschillen veroorzaken. Conversie naar int gooit breukdelen weg en kan een veel groter verschil dan de toegestane fout verbergen.

Vraag 7 van 10

Wat drukt printf("|%04d|", 23); af tussen de verticale strepen?
  1. | 23|
  2. |2300|
  3. |23 |
  4. |0023|
Toon antwoordVerberg antwoord
Antwoord: D
De breedte vraagt om minstens vier tekens en de nulvlag levert voorafgaande nullen, dus 23 wordt 0023. Gewoon rechts uitlijnen zou twee voorafgaande spaties gebruiken, maar dat is het gedrag van %4d zonder de nulvlag. De breedte voegt geen nullen achter de waarde toe.

Vraag 8 van 10

Gegeven int a = 2, b = 9; ontvangt een aanroep van scanf("%d:%d", &a, &b) de invoer “17 42”. Welke retourwaarde en opgeslagen waarden zijn het resultaat?
  1. Retourwaarde 2; a = 17, b = 42.
  2. Retourwaarde 1; a = 17, b = 9.
  3. Retourwaarde 0; a = 2, b = 9.
  4. Retourwaarde 1; a = 17, b = 0.
Toon antwoordVerberg antwoord
Antwoord: B
De eerste %d slaat 17 op. Daarna bevat de invoer een spatie waar het formaat een letterlijke dubbele punt vereist, dus stopt de verwerking en behoudt b de oude waarde negen. Het teruggegeven aantal is één; witruimte tussen getallen kan de door het formaat vereiste interpunctie niet vervangen.

Vraag 9 van 10

Wat drukt dit af op een systeem met 8-bits unsigned char en 32-bits int?

unsigned char a = 230, b = 40;
int sum = a + b;
unsigned char small = sum;
printf("%d %u\n", sum, (unsigned)small);
  1. 14 14
  2. 270 270
  3. 14 270
  4. 270 14
Toon antwoordVerberg antwoord
Antwoord: D
Beide tekenwaarden worden vóór de optelling tot int gepromoveerd, dus ontvangt sum 270 zonder modulaire terugloop bij 255. Conversie naar small reduceert 270 vervolgens modulo 256, wat 14 geeft. Als je voor beide waarden 14 voorspelt, pas je het kleine opslagbereik toe op de gepromoveerde optelling, waar dat niet hoort.

Vraag 10 van 10

De opdracht gcc -std=c17 -c sensor.c slaagt. Wat heeft die opgeleverd en welke bouwfase is nog nodig vóór gewone uitvoering?
  1. Een uitvoerbaar bestand; voorbewerking is nog nodig.
  2. Assembleertekst; assemblage is nog nodig.
  3. Een objectbestand; koppeling is nog nodig.
  4. Voorbewerkte C-code; compilatie is nog nodig.
Toon antwoordVerberg antwoord
Antwoord: C
De optie -c compileert en assembleert de broncode tot objectcode, normaal gesproken sensor.o, en stopt vóór de koppeling. Koppeling is nog nodig om het uitvoerbare bestand te maken en externe verwijzingen op te lossen. Assembleertekst is de eerdere uitvoer die met -S wordt gekozen, niet met -c.
Set 2: Lees invoer en reken met C-typen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een variabele is gedeclareerd als double distance_m;. Welke scanf-conversie en welk argument passen bij het type?
  1. scanf("%f", &distance_m)
  2. scanf("%lf", &distance_m)
  3. scanf("%lf", distance_m)
  4. scanf("%d", &distance_m)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Wat zijn -23 / 6 en -23 % 6 in C17?
  1. −4 en 1
  2. −3 en 5
  3. −4 en −5
  4. −3 en −5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Welke expressie berekent voor int distance = 19, time = 4; een snelheid als double waarbij het breukdeel behouden blijft?
  1. (double)distance / time
  2. (double)(distance / time)
  3. distance / time
  4. (int)(distance / (double)time)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een int die met %d wordt gelezen, accepteert de invoer “46kg”. Welke waarde wordt opgeslagen en welke invoer blijft direct na de conversie ongelezen?
  1. 46; er blijft niets over.
  2. 0; “46kg” blijft over.
  3. 46; “kg” blijft over.
  4. Geen waarde; “kg” blijft over.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Welke voorwaarde controleert voor niet-negatieve int-waarden total en count of het gehele gemiddelde boven 5 ligt zonder door nul te delen?
  1. total / count > 5 && count != 0
  2. count != 0 && total / count > 5
  3. count != 0 & total / count > 5
  4. count == 0 || total / count > 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Welke waarde levert 2 + 5 * 3 % 7 op?
  1. 1
  2. 0
  3. 7
  4. 3
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Welke tekst drukt printf("|%6.1f|", 8.375); af bij gewone afronding op het dichtstbijzijnde tiende?
  1. | 8.375|
  2. |8.4 |
  3. | 8.4|
  4. |0008.4|
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Welke waarden worden afgedrukt?

int n = 8;
int a = n++;
int b = ++n;
a *= b % 4;
printf("%d %d %d\n", a, b, n);
  1. 16 10 10
  2. 18 10 10
  3. 16 9 10
  4. 24 11 11
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Wat drukt dit af met 32-bits int en unsigned int?

int delta = -3;
unsigned int limit = 2;
printf("%d %u\n", delta < limit, limit + delta);
  1. 1 4294967295
  2. 1 -1
  3. 0 1
  4. 0 4294967295
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een waarde zonder teken is flags = 10u. Welke waarde ontstaat door bit 2 te zetten, waarbij de laagste bit als bit 0 wordt geteld?
  1. 12
  2. 14
  3. 8
  4. 2
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Volg takken, switches en lussen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een score is 76. Welk teken wordt toegewezen?

if (score >= 70) grade = 'C';
else if (score >= 85) grade = 'B';
else grade = 'F';
  1. C
  2. B
  3. F
  4. Er wordt geen teken toegewezen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Wat wordt afgedrukt wanneer mode gelijk is aan 2?

switch (mode) {
case 1: printf("A"); break;
case 2: printf("B");
case 3: printf("C"); break;
default: printf("D");
}
  1. B
  2. BCD
  3. BC
  4. ABC
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Welke eindwaarden hebben n en passes?

int n = 45, passes = 0;
while (n > 2) {
    n /= 2;
    passes++;
}
  1. n = 1, passes = 5
  2. n = 2, passes = 5
  3. n = 3, passes = 4
  4. n = 2, passes = 4
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Hoeveel iteraties worden uitgevoerd in for (int i = 4; i < 19; i += 4)?
  1. 3
  2. 4
  3. 5
  4. 15
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Wat wordt afgedrukt?

int level = 2;
if (level = 0) {
    printf("Low");
} else {
    printf("Other");
}
  1. Low
  2. LowOther
  3. Er wordt niets afgedrukt.
  4. Other
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Welk totaal wordt afgedrukt?

int total = 0;
for (int r = 1; r <= 4; r++) {
    for (int c = 1; c <= r; c++) {
        if (c == 3) break;
        total += c;
    }
}
printf("%d\n", total);
  1. 10
  2. 7
  3. 13
  4. 20
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een lus moet in de lusinhoud eenmaal om invoer vragen voordat het eerste antwoord kan worden getest. Welke lusvorm drukt die volgorde rechtstreeks uit?
  1. while met een controle vóór de lusinhoud
  2. do-while
  3. for met een controle vóór de lusinhoud
  4. switch met een default-label
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Welke som ontstaat?

int sum = 0;
for (int i = 2; i <= 8; i++) {
    if (i % 3 == 0) continue;
    sum += i;
}
  1. 35
  2. 9
  3. 26
  4. 8
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Hoe vaak wordt “tick” afgedrukt?

int running = 1, passes = 0;
while (running && passes < 3) {
    switch (passes) {
    case 1: break;
    default: printf("tick\n");
    }
    passes++;
}
  1. 1
  2. 3
  3. 0
  4. 2
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

De invoer is “9 4 0 7”. Welk aantal en welk gemiddelde worden afgedrukt?

int x, sum = 0, count = 0;
while (scanf("%d", &x) == 1 && x != 0) {
    sum += x;
    count++;
}
if (count) printf("%d %.1f\n", count, (double)sum / count);
  1. 2 6.5
  2. 3 4.3
  3. 3 6.7
  4. 4 5.0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Onderzoek conversies en bereikfouten10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Welke waarde levert 0u - 1u op wanneer unsigned int 32 waardebits heeft?
  1. −1
  2. 2.147.483.647
  3. 4.294.967.295
  4. 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Welke expressie berekent met 32-bits int-waarden a = 60000 en b = 40000 hun exacte product veilig in een gangbare 64-bits long long?
  1. (long long)(a * b)
  2. (long long)a * b
  3. a * b
  4. (double)(a * b)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Welk resultaat levert flags & 2u == 0u op voor flags = 6u zonder teken?
  1. 1
  2. 2
  3. 6
  4. 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Welke controle detecteert voor willekeurige int-waarden a en b overloop bij optelling met teken voordat a + b wordt geëvalueerd?
  1. (b > 0 && a > INT_MAX - b) || (b < 0 && a < INT_MIN - b)
  2. (b > 0 && a > INT_MAX - b) || (b < 0 && a > INT_MIN - b)
  3. (b > 0 && a >= INT_MAX - b) || (b < 0 && a <= INT_MIN - b)
  4. (b > 0 && a < INT_MAX - b) || (b < 0 && a < INT_MIN - b)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Op een platform met 16-bits unsigned short en 32-bits int bevatten twee unsigned short-variabelen elk 48000. Wat gebeurt er wanneer hun product zonder expliciete typeconversies wordt geëvalueerd?
  1. Ze worden zonder teken vermenigvuldigd en lopen terug modulo 65536.
  2. Ze worden gepromoveerd tot unsigned int en lopen terug bij 2³².
  3. Ze worden gepromoveerd tot int, waarvan het product overloopt.
  4. Ze worden gepromoveerd tot long long en geven het exacte product.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een C17-programma telt één op bij een int die al gelijk is aan INT_MAX. Op welk resultaat kan overdraagbare C-code vertrouwen?
  1. Het resultaat moet op elk systeem INT_MIN zijn.
  2. Het resultaat moet gelijk blijven aan INT_MAX.
  3. Het resultaat moet naar unsigned int worden geconverteerd.
  4. De taal garandeert geen resultaat.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Twee niet-negatieve int-eindpunten voldoen aan low <= high. Op een systeem met 32-bits int zijn ze 1.800.000.000 en 2.000.000.000. Welke expressie berekent hun midden zonder overloop?
  1. (low + high) / 2
  2. low + (high - low) / 2
  3. low + high / 2
  4. low / 2 + high
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een C17-programma converteert de representeerbare double-waarde −6,875 naar int. Welke waarde wordt opgeslagen?
  1. −6
  2. −7
  3. 6
  4. −6,875
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een positieve int-accumulator voor een faculteit is f en de volgende vermenigvuldiger is de positieve int i. Welke voorwaarde stelt vóór de vermenigvuldiging vast dat f * i groter zou zijn dan INT_MAX?
  1. f >= INT_MAX / i
  2. f / i > INT_MAX
  3. f > INT_MAX / i
  4. f * i > INT_MAX
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een int-berekening geeft in één versie de verwachte uitvoer, maar veroorzaakt onder -fsanitize=undefined een melding van overloop bij getallen met teken. Welke conclusie moet de reparatie sturen?
  1. De geslaagde versie bewijst dat het resultaat overdraagbaar is.
  2. De melding is op te lossen met een bredere printf-conversie.
  3. De melding garandeert dat de int naar een bepaalde waarde terugliep.
  4. De bereikte overloop moet in de berekening worden voorkomen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 5: Combineer invoer, programmaverloop en veilig rekenen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een programma vraagt in één scanf-aanroep om twee double-meetwaarden. Welke retourwaarde bevestigt dat beide toewijzingen zijn voltooid?
  1. 0
  2. 2
  3. 1
  4. EOF
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Welke waarde wordt afgedrukt?

int total = 0;
for (int r = 2; r <= 5; r++) {
    for (int c = 1; c < r; c++) {
        if (c % 2 == 0) continue;
        total += r;
    }
}
printf("%d\n", total);
  1. 14
  2. 18
  3. 40
  4. 23
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een 32-bits int days is 30000. Welke expressie berekent de tijdsduur correct in milliseconden in een 64-bits long long?
  1. 86400000LL * days
  2. (long long)(86400000 * days)
  3. 86400000 * days
  4. (long long)(86400 * days) * 1000
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Wat garandeert dit C17-fragment over zijn uitvoer?

int n = 4;
printf("%d %d\n", n++, n++);
  1. Het drukt 4 en 5 af omdat argumenten van links naar rechts worden uitgevoerd.
  2. Het drukt 5 en 4 af omdat argumenten van rechts naar links worden uitgevoerd.
  3. Het heeft ongedefinieerd gedrag door niet-geordende wijzigingen.
  4. Het drukt 4 en 5 of 5 en 4 af als gedefinieerde alternatieven.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Wat wordt afgedrukt wanneer a = -2 en b = 7?

if (a > 0)
    if (b > 0) printf("Both");
    else printf("A only");
  1. A only
  2. Both
  3. BothA only
  4. Er wordt niets afgedrukt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Waarom maakt deze lus geen voortgang meer nadat eenmaal nul is afgedrukt?

int i = 0;
while (i < 4) {
    if (i == 1) continue;
    printf("%d\n", i);
    i++;
}
  1. Bij één verlaat continue de lus vóór de volgende controle.
  2. Bij één slaat continue de bijwerking over, zodat i één blijft.
  3. Bij nul verhindert de voorwaarde de eerste bijwerking.
  4. Bij vier zet de lusinhoud i terug op één.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een voorraadwaarde zonder teken is 7u en een gevraagde hoeveelheid is 11u. Welke werkwijze voorkomt een modulair teruggelopen voorraad en behoudt een geldige aftrekking wanneer er genoeg voorraad is?
  1. Trek alleen af wanneer stock >= requested.
  2. Trek af en wijs af wanneer de nieuwe voorraad negatief is.
  3. Trek alleen af wanneer stock <= requested.
  4. Trek af en vereis dat de nieuwe voorraad ongelijk aan nul is.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Welke waarde laat deze code in flags achter bij een 32-bits unsigned int?

unsigned int flags = 13u;
flags &= ~(1u << 2);
flags |= 1u << 1;
  1. 15
  2. 9
  3. 11
  4. 7
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een menu begint met running = 1 en actions = 0. De invoer is “s q s”. Wat is actions na deze lus?

char cmd;
while (running && scanf(" %c", &cmd) == 1) {
    switch (cmd) {
    case 'q': running = 0; break;
    case 's': actions++; break;
    }
}
  1. 0
  2. 1
  3. 2
  4. 3
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Welke waarde wordt aan rate toegewezen?

int completed = 14, planned = 20;
double rate = completed / planned * 100.0;
  1. 70,0
  2. 0,7
  3. 14,0
  4. 0,0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

91 Kernbegrippen, elk met een definitie

A

Aantal geslaagde toewijzingen
Het aantal geslaagde toewijzingen dat scanf teruggeeft, is het aantal bestemmingen dat met succes is gevuld voordat de aanroep stopte.
Een aantal geslaagde toewijzingen van twee bij het lezen van drie meetwaarden laat de derde bestemming onbevestigd.
Accumulator
Een accumulator bewaart een lopend resultaat over lusiteraties heen en begint met een geschikte beginwaarde buiten het herhaalde werk.
De accumulator voor een cijfersom begint op nul en telt bij elke iteratie één opgehaald cijfer op.
Afkappen naar nul
Afkappen naar nul verwijdert het breukdeel van een getal, waardoor een negatief getal met een breukdeel een kleinere absolute waarde krijgt.
Door afkappen naar nul geeft conversie van de representeerbare waarde −7,9 naar int de waarde −7 in plaats van −8.
Afrondingsfout
Een afrondingsfout is het verschil dat ontstaat wanneer een waarde of berekeningsresultaat wordt vervangen door een nabijgelegen representeerbare waarde.
Meerdere decimale optellingen kunnen een kleine afrondingsfout opleveren waardoor een exacte gelijkheidscontrole mislukt.
Afsluitstatus
Een afsluitstatus is de waarde die een programma bij beëindiging aan de aanroepende omgeving meldt om succes of falen aan te geven.
Een afsluitstatus nul uit main laat een aanroepend script herkennen dat de uitvoering is geslaagd.
Associativiteit
Associativiteit bepaalt hoe operatoren op hetzelfde prioriteitsniveau groeperen, zonder doorgaans de evaluatievolgorde vast te leggen.
Door de associativiteit van deling van links naar rechts groepeert 24 / 4 / 2 als (24 / 4) / 2, wat drie geeft.

B

Bereikcontrole
Een bereikcontrole test of invoer of een voorgesteld resultaat aan grenzen voldoet voordat een onveilige bewerking wordt uitgevoerd.
Een bereikcontrole voor voorraad vergelijkt de beschikbare en gevraagde hoeveelheden voordat een aftrekking zonder teken modulair kan teruglopen.
Bitmasker
Een bitmasker is een geheelgetalpatroon waarmee je bepaalde bits in een andere waarde selecteert, zet, wist of bekijkt.
Het bitmasker 1u << 5 selecteert bit vijf, waarbij de minst significante bit als positie nul wordt geteld.
Bitsgewijze operator
Een bitsgewijze operator werkt op afzonderlijke bits van gehele operanden om hun bitpatronen te combineren, om te keren of te verschuiven.
De bitsgewijze operator & behoudt alleen de gemeenschappelijke gezette bits van twaalf en tien en geeft acht.
Bitverschuiving
Een bitverschuiving verplaatst het bitpatroon van een geheel getal een aantal posities naar links of rechts, volgens de regels voor breedte en teken van het gepromoveerde type.
Een waarde zonder teken twee posities naar rechts verschuiven gooit de twee laagste bits van de representatie weg.
Booleaanse waarde
Een Booleaanse waarde stelt onwaar of waar voor; conversie naar bool in C zet nul om naar onwaar en elke waarde anders dan nul naar waar.
Na opname van <stdbool.h> in C17 slaat bool ready = 2; de Booleaanse waarde waar op.
break-instructie
Een break-instructie verlaat onmiddellijk de dichtstbijzijnde omsluitende switch of lus en gaat verder na die constructie.
Een break-instructie binnen een menuswitch verlaat die switch terwijl de omsluitende while-lus doorgaat.
Broncode
Broncode is de voor mensen leesbare C-tekst die een compiler vertaalt naar instructies waarmee een programma wordt gebouwd.
Nadat je in de broncode de begroeting hebt gewijzigd die een programma afdrukt, bouw je het programma opnieuw voordat je de nieuwe versie uitvoert.

C

case-label
Een case-label noemt een afzonderlijke geheeltallige constante expressie die als ingang in een switch kan dienen.
Het case-label case 4: is geldig in C17; een gewone const int-variabele kan de waarde ervan niet leveren.
Compilatiefout
Een compilatiefout is een probleem dat tijdens het vertalen van broncode wordt ontdekt en een geslaagd nieuw bouwproces verhindert.
Een afsluitend haakje zonder bijbehorend openingshaakje veroorzaakt een compilatiefout voordat een nieuw uitvoerbaar bestand kan worden gebouwd.
Compiler
Een compiler vertaalt een programma uit een brontaal naar een andere vorm, zoals assembleertaal of machinecode.
De compiler controleert een C-declaratie en meldt een ontbrekende puntkomma voordat een nieuw uitvoerbaar bestand wordt gemaakt.
continue-instructie
Een continue-instructie slaat de resterende lusinhoud over en gaat naar het volgende voortzettingspunt van de lus.
Een continue-instructie in een for-lus bereikt de bijwerking in de kop; in een while-lus keert die terug naar de controle.
Conversie buiten het waardenbereik
Een conversie buiten het waardenbereik probeert een bronwaarde voor te stellen die buiten de toegestane waarden van het doeltype ligt.
Een conversie buiten het waardenbereik van een zeer grote double naar int is ongedefinieerd wanneer het gehele deel niet past.
Conversiespecificatie
Een conversiespecificatie is een onderdeel van een formaat dat met een procentteken begint en het vereiste type en de weergave van een waarde aangeeft.
De conversiespecificatie %lld past bij een long long-argument; %d kan dat niet automatisch converteren.

D

Declaratie
Een declaratie introduceert een naam en het bijbehorende type, zodat C kan controleren hoe het benoemde object of de benoemde functie wordt gebruikt.
De declaratie int count = 0; geeft de naam een geheelgetaltype en levert ook de beginwaarde.
default-label
Een default-label biedt een ingang in een switch voor een sturende waarde die met geen enkel case-label overeenkomt.
Een default-label kan een onverwacht menunummer naar een foutmelding sturen in plaats van de switch over te slaan.
do-while-lus
Een do-while-lus voert de lusinhoud uit voordat de herhalingsvoorwaarde wordt gecontroleerd en garandeert minstens één uitvoering van de lusinhoud.
Een do-while-lus leest het eerste antwoord voor een bereikcontrole in de lusinhoud voordat wordt besloten of opnieuw wordt gevraagd.
Door de implementatie bepaald gedrag
Door de implementatie bepaald gedrag is een keuze die de C-implementatie moet documenteren waar de standaard meerdere gedragingen toestaat.
Conversie van een geheel getal buiten het bereik naar een type met teken heeft in C17 door de implementatie bepaald gedrag of wekt een signaal op.
Doorvallen
Doorvallen is het doorgaan van de uitvoering vanuit één switch-geval naar de volgende code zonder tussenliggende overdracht van de besturing.
Doorvallen na een ontbrekende break kan de afdrukinstructie van het volgende geval uitvoeren zonder het label opnieuw te testen.
Drijvendekommawaarde
Een drijvendekommawaarde stelt een reële grootheid voor met een vaste precisie en een schaal, vaak met afronding.
Een lengte die als drijvendekommawaarde is opgeslagen, kan het getypte decimale getal benaderen zonder er exact mee overeen te komen.

E

Eén-ernaastfout
Een één-ernaastfout zorgt ervoor dat een lus of grensberekening één waarde te veel of te weinig meeneemt.
Als je i < n in een tellende lus vanaf nul vervangt door i <= n, ontstaat een één-ernaastfout.
Einde van de invoer
Het einde van de invoer betekent dat de inkomende gegevensstroom geen tekens meer bevat om te lezen; dit verschilt van gewone niet-overeenkomende tekst.
Een lus voor numerieke invoer stopt aan het einde van de invoer, maar kan ook eerder stoppen bij niet-numerieke tekst.
Escapesequentie
Een escapesequentie gebruikt een backslash in een C-tekenliteral of C-stringliteral om een speciaal teken voor te stellen.
De escapesequentie \n voegt een nieuweregelteken toe binnen een C-string; printf gebruikt %% voor een procentteken.
Evaluatievolgorde
Evaluatievolgorde beschrijft wanneer operanden worden geëvalueerd en neveneffecten plaatsvinden; dit staat los van de groepering van expressies.
Functieargumenten hebben in C17 geen vereiste evaluatievolgorde van links naar rechts, ondanks hun geschreven positie.
Expliciete typeconversie
Een expliciete typeconversie vraagt om conversie naar een benoemd type met een type tussen haakjes vóór een expressie.
De expliciete typeconversie in (double)sum / count werkt vóór de deling, waardoor een quotiënt met een breukdeel behouden blijft.

F

for-lus
Een for-lus plaatst initialisatie, een herhalingsvoorwaarde en een bijwerking in één kop bij een herhaalde lusinhoud.
Een for-lus die bij nul begint met i < n en i++, voert voor niet-negatieve n de lusinhoud n keer uit als de bijwerkingen passen.
Formaatstring
Een formaatstring combineert letterlijke tekst en conversiespecificaties die bepalen hoe een invoer- of uitvoeraanroep waarden verwerkt.
De formaatstring in printf("Mass: %.1f kg\n", mass) levert een label, decimale precisie en een eenheid.

G

Gebruikelijke rekenkundige conversies
De gebruikelijke rekenkundige conversies kiezen gemeenschappelijke typen voor operanden bij veel rekenkundige en vergelijkingsbewerkingen.
De gebruikelijke rekenkundige conversies zetten een negatieve int om naar een type zonder teken wanneer die met unsigned int wordt vergeleken.
Geheel getal met teken
Een geheel getal met teken is een geheelgetaltype dat zowel negatieve als niet-negatieve waarden binnen een vast bereik voorstelt.
Een geheel getal met teken kan een negatieve temperatuurwaarde opslaan, maar latere optellingen moeten nog steeds binnen het bereik passen.
Geheel getal zonder teken
Een geheel getal zonder teken stelt alleen niet-negatieve gehele getallen voor en gebruikt reductie modulo wanneer een rekenkundig resultaat buiten het bereik valt.
Een geheel getal zonder teken werkt goed als bitmasker, maar te veel voorraad aftrekken kan modulair teruglopen naar een zeer grote waarde.
Geheelgetaltype met exacte bitbreedte
Een geheelgetaltype met exacte bitbreedte is een geheelgetaltype dat, indien beschikbaar, met typedef is benoemd en waarvan de naam de exacte bitbreedte en de aanwezigheid van een teken vastlegt.
Het geheelgetaltype met exacte bitbreedte uint8_t bevat, wanneer beschikbaar, waarden van nul tot en met 255.
Geheeltallige deling
Geheeltallige deling deelt gehele operanden en kapt het breukdeel van het quotiënt af naar nul.
Geheeltallige deling in 22 / 5 geeft vier, ook wanneer het voltooide resultaat aan een double wordt toegewezen.
Geheeltallige literal
Een geheeltallige literal is een constante voor een geheel getal die in de broncode wordt geschreven met een grondtalnotatie en een optioneel typeachtervoegsel.
De geheeltallige literal 0x30 gebruikt hexadecimale notatie, terwijl 48u die waarde decimaal zonder teken schrijft.
Geldigheidsgebied van een blok
Het geldigheidsgebied van een blok maakt een lokale naam beschikbaar vanaf de declaratie tot het einde van het omsluitende blok.
Het geldigheidsgebied van een blok verhindert dat je een tijdelijke variabele die binnen een if-blok is gedeclareerd, gebruikt nadat dat blok is geëindigd.
Geneste lus
Een geneste lus is een lus binnen een andere lus, waarbij de binnenste reeks opnieuw wordt uitgevoerd voor elke bereikte buitenste iteratie.
Een geneste lus die voor elk van drie rijen vier binnenste iteraties uitvoert, voert de binnenste lusinhoud twaalf keer uit.
Grenswaardetest
Een grenswaardetest test waarden op of nabij een betekenisvolle grens om gedrag te controleren waar een regel of bereik verandert.
Een grenswaardetest voor optelling omvat een som gelijk aan INT_MAX en een paar waarden dat die grens met één zou overschrijden.

I

Identifier
Een identifier is een naam in C-code, zoals een variabelenaam, waarvoor spellingregels gelden en waarbij hoofdletters en kleine letters worden onderscheiden.
De identifier distance_m is geldig; 2distance kan geen naam zijn omdat die met een cijfer begint.
Impliciete conversie
Een impliciete conversie verandert het type van een waarde automatisch wanneer een bewerking of toewijzing een ander type vereist.
Als je een int bij een double optelt, wordt vóór de optelling een impliciete conversie op de gehele operand toegepast.
Initialisatie
Initialisatie geeft een object zijn beginwaarde als onderdeel van de declaratie, voordat latere toewijzingen die waarde veranderen.
Initialisatie van total op nul vóór een lus geeft de eerste optelling een bepaalde beginwaarde.
Instructieblok
Een instructieblok groepeert declaraties en instructies tussen accolades, zodat ze samen als één gestuurde inhoud kunnen dienen.
Een instructieblok houdt een melding en een bijwerking van een teller samen binnen dezelfde voorwaardelijke tak.
Integerpromotie
Een integerpromotie converteert een klein geheelgetaltype naar int, of naar unsigned int als int niet het volledige bereik kan voorstellen.
Op een systeem met 16-bits short en 32-bits int laat integerpromotie unsigned short-operanden als int vermenigvuldigen.
Iteratie
Een iteratie is één uitvoering van de inhoud van een lus.
Een for-lus die 3, 6, 9 en 12 doorloopt, heeft vier iteraties, ook al bereikt de laatste bijwerking van de teller 15.

K

Kommaoperator
De kommaoperator evalueert de linkeroperand vóór de rechteroperand en levert de waarde van de rechteroperand op.
De kommaoperator ordent de bijwerkingen in i++, j--; komma’s tussen printf-argumenten doen dat niet.
Kortsluitevaluatie
Kortsluitevaluatie slaat de rechteroperand van een logische operator over wanneer de linkeroperand het resultaat al bepaalt.
Kortsluitevaluatie in count != 0 && sum / count > 4 slaat de deling over wanneer count nul is.

L

Linker
De linker combineert objectcode en lost verwijzingen naar benoemde code en gegevens op om een voltooid programma te maken.
De linker lost de verwijzing naar printf op; de bijbehorende bibliotheekcode kan door een gedeelde bibliotheek worden geleverd.
Logische fout
Een logische fout zorgt ervoor dat een programma iets anders berekent of kiest dan het bedoelde resultaat, ook als het wordt uitgevoerd.
Een controle van scorecategorieën in de verkeerde volgorde veroorzaakt een logische fout die de verkeerde beoordeling toewijst.
Logische operator
Een logische operator combineert waarheidswaarden of keert die om en levert via &&, || of ! nul of één op.
De logische operator in ready && valid test of beide opgeslagen waarden ongelijk aan nul zijn.
Losse else
Een losse else is een else in geneste voorwaardelijke instructies waarvan de koppeling voor een lezer zonder accolades onduidelijk kan zijn.
De regel voor de losse else koppelt een else aan de dichtstbijzijnde if zonder bijbehorende else, ongeacht de inspringing.

M

Macro
Een macro definieert een vervanging voor de preprocessor die vóór de gewone compilatie in latere broncode wordt ingevoegd.
Met #define LIMIT 8 wordt het token LIMIT vervangen door 8 overal waar die macro van toepassing is.
Modulaire terugloop bij gehele getallen zonder teken
Modulaire terugloop bij gehele getallen zonder teken is reductie modulo van een rekenkundig resultaat zonder teken, waardoor het terug in het niet-negatieve bereik van het type wordt gebracht.
Eén aftrekken van nul zonder teken demonstreert modulaire terugloop bij gehele getallen zonder teken door de maximale waarde op te leveren.

N

NaN
Een NaN is een IEEE-drijvendekommawaarde die “geen getal” betekent en wordt gebruikt voor resultaten zoals nul gedeeld door nul.
Een NaN is bij vergelijking ongelijk aan zichzelf, zodat gewone gelijkheid die waarde niet als een gewone meetwaarde kan behandelen.
Neveneffect
Een neveneffect is een verandering zoals het wijzigen van opgeslagen gegevens of het uitvoeren van invoer of uitvoer tijdens expressie-evaluatie.
Een incrementatie verandert een variabele als neveneffect, dus twee tegenstrijdige incrementaties vereisen een vastgelegde volgorde.
Niet-passende invoer
Van niet-passende invoer is sprake wanneer de beschikbare invoer niet overeenkomt met de conversie of het letterlijke teken dat scanf verwacht.
Letters aan het begin van een %d-conversie veroorzaken niet-passende invoer en blijven ongelezen voor een nieuwe poging.

O

Oneindige lus
Een oneindige lus herhaalt zonder een uitgang te bereiken, vaak omdat de geteste toestand niet volgens de bedoeling verandert.
Een continue vóór de enige bijwerking kan een oneindige lus maken die één ongewijzigde waarde blijft testen.
Ongedefinieerd gedrag
Ongedefinieerd gedrag ontstaat wanneer een C-programma een bewerking uitvoert waaraan de taal geen eisen stelt.
Een overlopend product met teken heeft ongedefinieerd gedrag, ook als één uitvoering zonder optimalisatie modulair lijkt terug te lopen.
Operand
Een operand is een waarde of expressie waarop een operator wordt toegepast; het type helpt bepalen hoe de bewerking zich gedraagt.
In sum / count bepalen de typen van de operanden of de deling een resultaat met een breukdeel behoudt.
Operatorprioriteit
Operatorprioriteit bepaalt welke operatoren sterker aan hun operanden binden wanneer haakjes de groepering niet aangeven.
Door de hogere prioriteit van vermenigvuldiging wordt 4 * 2 in 3 + 4 * 2 vóór de optelling gegroepeerd.
Overloop bij gehele getallen
Overloop bij gehele getallen ontstaat wanneer het wiskundige resultaat van een geheeltallige berekening buiten het bereik van het resultaattype ligt.
Een int-product kan overloop bij gehele getallen veroorzaken voordat het resultaat een long long-bestemming bereikt.

P

Postfixincrementatie
Een postfixincrementatie telt één op bij een variabele en levert tegelijk de oude waarde op voor gebruik in de omringende expressie.
Met n = 3 geeft de postfixincrementatie in old = n++; aan old de waarde drie en laat n op vier staan.
Precisie
Precisie is de hoeveelheid significante informatie die een getalformaat kan behouden, los van de grootste waarde die het kan voorstellen.
Door de beperkte precisie van float gaan stappen van één geheel getal verloren bij waarden die nog ver onder het maximum van het type liggen.
Preprocessor
De preprocessor verwerkt richtlijnen zoals #include en macrovervanging vóór de belangrijkste vertaalfasen van C.
De preprocessor voegt declaraties uit een standaardheader in voordat de compiler het gebruik ervan controleert.

R

Rekenen met overloopcontrole
Rekenen met overloopcontrole voert een berekening uit en meldt of het exacte wiskundige resultaat in het gekozen doeltype past.
De C23-bewerking ckd_mul voor rekenen met overloopcontrole kan een product melden dat niet in de bestemming past.
Relationele operator
Een relationele operator vergelijkt numerieke waarden met <, <=, > of >= en levert de int-waarde nul of één op.
De relationele operator in height >= 1 controleert één grens; een andere vergelijking controleert de andere.
Rest
Een rest is de waarde die overblijft bij de geheeltallige %-bewerking van C; een resultaat anders dan nul heeft het teken van het deeltal.
De rest -22 % 5 is −2 omdat het afgekaptе quotiënt −4 is en aan de delingsidentiteit moet worden voldaan.

S

Samengestelde toewijzing
Een samengestelde toewijzing combineert een operator met het terug opslaan van het resultaat in de bestemming links.
De samengestelde toewijzing total += item; werkt het lopende totaal bij zonder item te veranderen.
Sanitizer
Een sanitizer is een controlehulpmiddel voor tijdens de uitvoering dat bij compilatie wordt toegevoegd en bepaalde ongeldige bewerkingen op uitgevoerde paden meldt.
Een sanitizer voor ongedefinieerd gedrag kan de vermenigvuldiging aanwijzen waarbij een tellerberekening met teken overloopt.
Standaardinvoer
Standaardinvoer is de gebruikelijke inkomende tekststroom van een programma, vaak verbonden met het toetsenbord of met omgeleide invoer.
Een scanf-aanroep leest uit de standaardinvoer en converteert de volgende overeenkomende tekens naar opgeslagen waarden.
Stopwaarde
Een stopwaarde is een aangewezen waarde die het einde van gegevens aangeeft en vóór de gewone verwerking wordt herkend.
Een stopwaarde nul kan een berekening van een gemiddelde beëindigen zonder de som of het aantal gegevenswaarden te verhogen.
switch-instructie
Een switch-instructie kiest een beginlabel door één gehele sturende waarde te vergelijken met vaste gehele case-waarden.
Een switch-instructie kan de opdrachttekens kleine letter r en hoofdletter R dezelfde actie geven.

T

Tak
Een tak is één pad van instructies dat door een keuze wordt geselecteerd, zoals het if-deel of het else-deel van een voorwaardelijke instructie.
Een programma met een else-if-keten voor scores bereikt slechts één tak, te beginnen bij de eerste ware voorwaarde.
Tekenconstante
Een tekenconstante is een tekenvorm tussen enkele aanhalingstekens waarvan de waarde een geheeltallige code is; gewone tekenconstanten in C hebben het type int.
De ASCII-tekenconstante 'A' kan met %c worden afgedrukt als de letter A of met %d als het getal 65.
Toewijzing
Een toewijzing berekent een waarde en slaat die op in een bestaande bestemming, met conversie naar het type van die bestemming.
De toewijzing count = count + 1; leest de oude waarde en vervangt die door een waarde die één groter is.
Tolerantie
Een tolerantie is een toegestaan numeriek verschil dat wordt gekozen op basis van de schaal en nauwkeurigheid die een vergelijking vereist.
Het programma vergelijkt fabs(measured - target) met de tolerantie in plaats van exacte gelijkheid te eisen.
Tussenresultaat
Een tussenresultaat is een waarde die binnen een grotere expressie wordt berekend vóór een latere bewerking of toewijzing.
De som in (low + high) / 2 is een tussenresultaat dat kan overlopen, ook als het midden past.
Tweecomplement
Tweecomplement is een binaire representatie met teken waarin de hoogste bit een negatief gewicht heeft.
In achtbits tweecomplement stelt het patroon 10000000 −128 voor.
Typeverbreding
Typeverbreding converteert een waarde naar een type met voldoende representeerbaar bereik voordat de waarde deelneemt aan een risicovolle berekening.
Typeverbreding van a naar long long vóór a * b verandert het type van het product; een expliciete typeconversie na het product komt te laat.

U

Uitvoerbaar bestand
Een uitvoerbaar bestand is een gebouwd programma in een vorm die het doelbesturingssysteem kan laden en op de bedoelde processor kan uitvoeren.
Als je ./hello uitvoert, start het uitvoerbare bestand in de huidige map zonder de C-compiler opnieuw aan te roepen.
Uitvoerbuffering
Uitvoerbuffering houdt geproduceerde tekst tijdelijk vast voordat die wordt doorgestuurd, waardoor een prompt zonder nieuweregelteken vertraagd kan verschijnen.
Een prompt kan door uitvoerbuffering wachten totdat fflush(stdout) die vóór een invoeraanroep verzendt.
Uitvoeringsspoor
Een uitvoeringsspoor legt de veranderende waarden en uitgevoerde stappen van een programma vast, zodat het gedrag op volgorde kan worden gecontroleerd.
In een cijfersomlus helpt een uitvoeringsspoor van de resterende cijfers en de lopende som na elke iteratie om een verkeerde bijwerking van de lus te vinden.

V

Variabele
Een variabele is een benoemde opslaglocatie met een gedeclareerd type dat bepaalt hoe de opgeslagen waarde wordt geïnterpreteerd.
De variabele mass_kg slaat een meetwaarde op, terwijl count bijhoudt hoeveel monsters zijn ingevoerd.
Veldbreedte
Een veldbreedte is het minimale aantal tekens dat voor een opgemaakte uitvoerwaarde wordt gereserveerd, inclusief het teken en de decimale punt.
Een veldbreedte van zes in %6d voegt vier spaties toe vóór 42, terwijl een waarde van zeven cijfers nog steeds volledig wordt afgedrukt.
Voorwaarde
Een voorwaarde is een expressie waarvan de waarde nul of ongelijk aan nul bepaalt welk pad in het programma wordt gevolgd.
De voorwaarde in if (items) is onwaar voor nul, terwijl een negatief aantal anders dan nul de tak uitvoert.
Voorwaardelijke operator
De voorwaardelijke operator gebruikt condition ? a : b om na controle van de voorwaarde één waarde-expressie te kiezen.
Bij een voorwaarde nul evalueert de voorwaardelijke operator in condition ? a : b de expressie b en slaat a over.

W

Waarschuwing
Een waarschuwing is een diagnostische melding die op een mogelijk probleem wijst en het bouwproces meestal laat doorgaan.
Een formaatwaarschuwing kan laten zien dat %d is gekoppeld aan een double-argument, een ongeldige combinatie.
while-lus
Een while-lus herhaalt de lusinhoud zolang een voorwaarde waar is en controleert die voorwaarde vóór elke uitvoering van de lusinhoud.
Een while-lus die cijfers verwerkt, stopt zodra geheeltallige deling de resterende waarde tot nul heeft teruggebracht.
Witruimte
Witruimte bestaat uit tekens zoals spaties, tabs en nieuweregeltekens die formaten afhankelijk van de conversie kunnen overslaan of lezen.
Numerieke scanf-conversies slaan voorafgaande witruimte over, maar %c leest een achtergebleven nieuweregelteken tenzij de conversie opdracht krijgt dat over te slaan.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag