Arrays en strings in C

Leer hoe C meerdere waarden in één array opslaat en tekst voorstelt als tekens die eindigen op een nulbyte. Doorloop arrays en tabellen, geef ze door aan functies, verwerk strings met eigen lussen en met de standaardfuncties voor strings, bekijk hoe UTF-8-tekst in bytes wordt opgeslagen en lees de argumenten die een programma via de opdrachtregel ontvangt. Leer daarbij waarom C arraygrenzen nooit controleert en hoe je gegevens zo leest en kopieert dat ze altijd passen.

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

Wat je leert

  1. Declareer arrays met een vaste grootte, benader elk element via zijn index, doorloop de arrays zonder voorbij het einde te gaan en deel ze met functies.
    • Declareer en initialiseer een array en benader de elementen met een index
      Maak arrays met een vast aantal elementen, geef ze beginwaarden en lees of wijzig afzonderlijke elementen via hun index.
    • Doorloop arrays zonder de grenzen te overschrijden
      Bezoek elk element met een tellende lus en gebruik dat patroon om op te tellen, de grootste waarde te vinden, te zoeken, te tellen en om te keren.
    • Geef arrays door aan functies
      Schrijf functies die arrays als parameters ontvangen, geef de lengte apart mee en weet wanneer de functie de gegevens van de aanroepende functie kan wijzigen.
  2. Behandel tekens als kleine geheeltallige codes, sla tekst op in char-arrays die eindigen op een nulteken, verwerk tekst teken voor teken en bekijk hoe UTF-8-tekst in bytes past.
    • Behandel tekens als numerieke codes
      Gebruik het type char en tekenconstanten als getallen, converteer cijfers en letters met berekeningen en classificeer tekens met <ctype.h>.
    • Sla tekst op als strings die eindigen op een nulteken
      Leg uit hoe een C-string in een char-array is opgebouwd, waarom die een afsluitend nulteken nodig heeft en hoe stringliterals en arrays verschillen.
    • Verwerk strings teken voor teken
      Doorloop een string tot het afsluitende nulteken om tekst te tellen, converteren, omkeren, filteren en versleutelen, en sluit elke nieuwe string af met '\0'.
    • Leg uit hoe UTF-8-tekst in char-arrays wordt opgeslagen
      Breng tekens, Unicode-codepunten en UTF-8-bytes met elkaar in verband en voorspel wat bytegebaseerde C-functies doen met tekst buiten ASCII.
  3. Bepaal de lengte van strings, kopieer, voeg samen, vergelijk en doorzoek ze met <string.h>, en zorg dat elke doelarray ruimte heeft voor het resultaat en het afsluitende nulteken.
    • Bepaal de lengte van strings, kopieer ze en voeg ze samen
      Gebruik strlen, strcpy en strcat correct en bepaal hoeveel ruimte elke doelarray nodig heeft.
    • Vergelijk en doorzoek strings
      Vergelijk strings met strcmp en strncmp, zoek tekens en deelstrings en splits tekst in tokens.
    • Kopieer binnen de grenzen en werk rechtstreeks met geheugenbytes
      Gebruik de begrensde functies strncpy, strncat en snprintf correct, detecteer afkapping en kopieer, vul en vergelijk hele arrays met de geheugenfuncties.
  4. Sla tabellen en lijsten met strings op in arrays met twee of meer dimensies, doorloop ze met geneste lussen en geef ze door aan functies.
    • Declareer en doorloop tweedimensionale arrays
      Declareer en initialiseer tabellen met waarden, leg uit hoe hun rijen in het geheugen zijn geplaatst en verwerk rijen en kolommen met geneste lussen.
    • Geef tabellen door aan functies en sla lijsten met strings op
      Schrijf functies die tweedimensionale arrays ontvangen en bewaar meerdere strings in een 2D-char-array of in een array van stringpointers.
  5. Lees via argc en argv de woorden die na de naam van een programma zijn getypt, controleer hoeveel het er zijn en zet ze veilig om in getallen en opties.
    • Lees argc en argv
      Declareer main met zijn twee parameters, beschrijf wat elk element van argv bevat en controleer het aantal argumenten voordat je een argument gebruikt.
    • Converteer en valideer numerieke argumenten
      Zet argumentstrings om in getallen met foutcontrole, wijs ongeldige invoer af en controleer het waardenbereik voordat je waarden gebruikt.
  6. Herken hoe C-programma’s buiten hun arrays lezen en schrijven, waarom dat gevaarlijk is en hoe je tekst zo leest en kopieert dat die altijd past.
    • Herken toegang buiten de grenzen en de gevolgen daarvan
      Leg uit waarom C toegang buiten de grenzen niet tegenhoudt, wat er kan gebeuren als dit voorkomt en welke fouten het veroorzaken.
    • Lees en kopieer tekst binnen de grenzen van de buffer
      Lees invoer met een maximale lengte, detecteer regels die te lang zijn en kopieer tekst alleen als die in de doelarray past.

Arrays en strings in C: uitleg

6 hoofdstukken

1

Sla verzamelingen waarden op in arrays en verwerk ze

Met arrays kan één lus op veel samenhangende waarden werken. De array bepaalt hoeveel opslagruimte er is, terwijl indexen afzonderlijke elementen selecteren en een teller bijhoudt hoeveel elementen in gebruik zijn. Leer hoe declaraties beginwaarden vastleggen, hoe lussen resultaten berekenen zonder een grens te overschrijden en hoe functies de elementen van de aanroepende functie delen. Kleine voorbeelden verbinden de code met de exacte posities die ze leest of beschrijft.

1.1 Declareer en initialiseer een array en benader de elementen met een index

Een array geeft één naam aan een vast aantal waarden van hetzelfde type. De elementen staan naast elkaar in het geheugen. Gebruik een array wanneer dezelfde bewerking op veel waarden van toepassing is, zoals bij het bijwerken van een verzameling scores.

Maak de elementen
int scores[5] = {90, 72, 85, 64, 99};
int primes[] = {2, 3, 5, 7};
int counts[6] = {0};

De eerste declaratie maakt vijf gehele getallen. De compiler telt vier waarden in de tweede initialisator, dus primes heeft vier elementen. In de derde is het eerste element expliciet nul en wordt ook elke ontbrekende initialisatiewaarde nul. Zo bevat int a[5] = {1, 2}; de waarden 1, 2, 0, 0, 0. Een benoemde initialisator, beschikbaar sinds C99, kiest een positie: int b[5] = {[2] = 7}; zet 7 in het derde element en nul in de andere.

Een automatische lokale declaratie zoals int data[5]; reserveert ruimte zonder beginwaarden te geven. Schrijf elk element voordat je het leest. Arrays die buiten alle functies zijn gedeclareerd en arrays die met static zijn gedeclareerd, worden op nul geïnitialiseerd als er geen expliciete initialisator is opgegeven.

Benader één waarde via de index

Indexen beginnen bij nul. Voor vijf elementen zijn de geldige posities 0 tot en met 4. scores[1] betekent de tweede score, niet de eerste. Je kunt een element overal gebruiken waar een gewone variabele van hetzelfde type kan worden gebruikt:

scores[1] = 75;
scores[1] += 5;
int last = scores[4];

De tweede score is nu 80. De laatste is nog steeds 99. Een index kan een variabele of een expressie zijn, dus scores[n - 1] benadert het laatste element als n het aantal elementen is en positief is. Bij scores[5] is er geen element om te benaderen.

Tel elementen en bytes

sizeof scores meet de hele array in bytes. Op een systeem met gehele getallen van 4 bytes nemen de vijf elementen 20 bytes in. Door te delen door de grootte van één element maak je de uitkomst onafhankelijk van de grootte van een geheel getal:

size_t n = sizeof scores / sizeof scores[0];
printf("%zu\n", n);

Dit drukt 5 af. size_t is het geheelgetaltype zonder teken dat voor groottes wordt gebruikt; %zu drukt het af. Deze berekening werkt waar scores de eigenlijke array aanduidt. Een functieparameter bevat niet de volledige grootte van de array.

Houd grootte en waarden gescheiden

Met een naam zoals #define NUM_SCORES 5 kunnen de declaratie en de bijbehorende lussen één grootte delen. In C17 maakt een lokale const int n = 5; int a[n]; daarentegen een array met variabele lengte: const maakt van n geen geheeltallige constante expressie. De grootte wordt gekozen wanneer de declaratie wordt uitgevoerd en blijft daarna vast gedurende de levensduur van de array. Een initialisatielijst is niet mogelijk. Ondersteuning voor arrays met variabele lengte is optioneel in C11 en C17; C23 voegt constexpr toe voor constanten tijdens compilatie.

Arrays kunnen niet als geheel worden toegewezen: b = a; is ongeldig. Kopieer hun elementen met een lus of een geschikte geheugenfunctie. Voor twee afzonderlijke arrays vergelijkt a == b adressen, geen elementwaarden. Vergelijk overeenkomstige elementen om te testen of de waarden gelijk zijn. Een bytevergelijking met memcmp test daarentegen hun opgeslagen representaties; gelijke drijvendekommawaarden zoals positieve en negatieve nul kunnen verschillende representaties hebben.

Belangrijkste punten

  • Array-indexen lopen van 0 tot één minder dan het aantal elementen.
  • Ontbrekende initialisatiewaarden worden nul; niet-geïnitialiseerde lokale elementen moet je vóór gebruik beschrijven.
  • Gebruik sizeof a / sizeof a[0] alleen waar a een echte array is.
  • Een array behoudt zijn grootte gedurende zijn levensduur.
  • Kopieer en vergelijk arraywaarden element voor element.

1.2 Doorloop arrays zonder de grenzen te overschrijden

Een tellende lus verbindt de posities van een array met een bewerking. Bepaal welke elementen bruikbare gegevens bevatten en bezoek dan precies die posities. Hetzelfde patroon ondersteunt totalen, zoekopdrachten en wijzigingen.

Bezoek de gebruikte elementen
int t[5] = {12, 15, 9, 14, 10};
int sum = 0;
for (int i = 0; i < 5; i++) {
    sum += t[i];
}
double average = sum / 5.0;

De lus gebruikt de indexen 0, 1, 2, 3 en 4. Het totaal loopt op via 12, 27, 36, 50 en 60, dus het gemiddelde is 12,0. Als je i <= 5 schrijft, voeg je een ongeldige toegang bij index 5 toe. Die extra iteratie is een één-ernaastfout.

Gebruik voor een gemiddelde een drijvendekommagetal bij de deling. Bij een totaal van 61 geeft 61 / 5 het gehele getal 12; 61 / 5.0 geeft 12,2. Als je het eerste resultaat daarna in een double opslaat, kun je het al weggelaten deel achter de komma niet terugkrijgen.

Zoek een maximum of een overeenkomst

Initialiseer bij een niet-lege array het maximum met de eerste waarde:

int max = t[0];
for (int i = 1; i < 5; i++) {
    if (t[i] > max) max = t[i];
}

De eerste vergelijking vervangt 12 door 15. De overige waarden zijn kleiner, dus 15 blijft staan. Beginnen met nul zou misgaan bij een lijst zoals {-8, -3, -11}: nul is groter dan elke opgeslagen waarde, maar staat niet in de lijst.

Bij lineair zoeken controleer je de waarden op volgorde:

int found = -1;
for (int i = 0; i < 5; i++) {
    if (t[i] == 14) {
        found = i;
        break;
    }
}

Hier wordt found gelijk aan 3. De waarde -1 geeft aan dat er geen overeenkomst is, omdat die geen geldige index kan zijn. Meld pas dat er niets is gevonden nadat alle kandidaten zijn gecontroleerd. Als je na het eerste ongelijke element -1 teruggeeft, mis je overeenkomsten verderop in de array.

Tel en herschik

Een frequentiearray slaat per categorie één teller op. Voor cijfers biedt int counts[10] = {0}; de tellers 0 tot en met 9. Nadat je d >= 0 && d < 10 hebt gecontroleerd, verhoogt counts[d]++ de teller voor cijfer d. De controle beschermt de array wanneer de cijferwaarde uit invoer komt.

Om een array om te keren, wissel je paren vanaf de uiteinden:

for (int i = 0; i < 5 / 2; i++) {
    int temp = t[i];
    t[i] = t[5 - 1 - i];
    t[5 - 1 - i] = temp;
}

De eerste wissel verwisselt 12 en 10. De tweede verwisselt 15 en 14. De middelste 9 blijft staan, met {10, 14, 9, 15, 12} als resultaat. Stop halverwege: als je alle vijf posities doorloopt, wissel je elk paar twee keer.

Houd het aantal gebruikte elementen apart van de capaciteit bij

Een array met ruimte voor 100 waarden kan op dit moment slechts 37 bruikbare elementen bevatten. De capaciteit is 100; het aantal gebruikte elementen is 37. Verwerk de posities onder count. Om één waarde toe te voegen, controleer je eerst count < CAPACITY, sla je de waarde op in a[count] en verhoog je daarna count. Als je tot de capaciteit doorloopt, kun je posities lezen die nooit bruikbare waarden hebben gekregen, ook al liggen die posities fysiek binnen de array.

Belangrijkste punten

  • Gebruik een strikte lusgrens met <.
  • Gebruik drijvendekommadeling wanneer een gemiddelde een deel achter de komma kan hebben.
  • Initialiseer het maximum van een niet-lege array met een element, ook als alle gegevens negatief zijn.
  • Beslis pas dat een zoekopdracht is mislukt nadat alle kandidaten zijn gecontroleerd.
  • Valideer een waarde voordat je die als index gebruikt.
  • Keer om door alleen de eerste helft van de paren te wisselen.
  • Het aantal geeft aan hoeveel elementen in gebruik zijn; de capaciteit is de beschikbare ruimte.

1.3 Geef arrays door aan functies

Een functie kan een array verwerken zonder van elk element een afzonderlijke kopie te ontvangen. Via de aanroep heeft de functie toegang tot de opslag van de aanroepende functie. Geef het aantal elementen mee, zodat de functie weet waar ze moet stoppen.

Geef de gegevens en hun lengte mee
#include <stddef.h>

double average(const int a[], size_t n) {
    if (n == 0) return 0.0;
    int sum = 0;
    for (size_t i = 0; i < n; i++) {
        sum += a[i];
    }
    return (double) sum / n;
}

De aanroepende functie kan int values[] = {7, 8, 10}; declareren en average(values, sizeof values / sizeof values[0]) aanroepen. De drie optellingen leveren 25 op; door sum vóór de deling naar double te converteren, krijg je ongeveer 8,33. Bij geen waarden 0,0 teruggeven is de gekozen werkwijze van deze functie voor lege invoer. De kleine waarden in dit voorbeeld houden de geheeltallige som binnen het waardenbereik.

In een parameterdeclaratie wordt int a[] omgezet naar int *a. Die parameter ontvangt het adres van het eerste element. Zelfs int a[10] controleert of vereist geen tien elementen. De aanroepende functie moet een lengte opgeven die overeenkomt met de beschikbare gegevens.

Waarom sizeof de lengte niet meer kent

Op de plaats van de aanroep meet sizeof values de array. Binnen de functie meet sizeof a de pointerparameter. Waar pointers 8 bytes en gehele getallen 4 bytes innemen, geeft deling van deze groottes 2, ongeacht het aantal elementen van de aanroepende functie. Dat resultaat beschrijft geen kopie van twee elementen en ook niet de oorspronkelijke array. Houd de lengte bij als expliciete parameter.

size_t, gedeclareerd door onder meer <stddef.h>, is het type zonder teken dat sizeof oplevert. Het kan objectgroottes voorstellen en %zu drukt het af. Door dit type voor de lusindex te gebruiken, voorkom je dat een index met teken wordt vergeleken met een lengte zonder teken.

Bepaal of elementen mogen veranderen

De const in const int a[] voorkomt toewijzingen aan elementen via deze parameter. Dit past bij een functie die een resultaat uit de gegevens berekent. Een functie die elementen moet bijwerken, laat die kwalificatie weg:

void zero(int a[], size_t n) {
    for (size_t i = 0; i < n; i++) {
        a[i] = 0;
    }
}

Na zero(values, 3) bevatten de drie elementen van de aanroepende functie allemaal nul. De array heeft nog steeds drie elementen. Een scalaire parameter zoals n is een afzonderlijke kopie; als je die kopie wijzigt, verandert een tellervariabele van de aanroepende functie niet.

Laat de aanroepende functie een uitvoerarray en de lengte ervan beschikbaar stellen om meerdere resultaten te produceren. Een functie kan elk resultaat in out[i] plaatsen. C-functies kunnen geen array op waarde teruggeven en het adres van een lokale array teruggeven werkt niet, omdat die array ophoudt te bestaan wanneer de functie terugkeert.

Tel terug met een index zonder teken

Een index zonder teken kan niet negatief worden. In for (size_t i = n - 1; i >= 0; i--) is de test altijd waar; na nul loopt de waarde modulair terug naar een groot positief getal. Bij n == 0 loopt zelfs de eerste aftrekking modulair terug. Begin in plaats daarvan bij het aantal:

for (size_t i = n; i > 0; i--) {
    printf("%d\n", a[i - 1]);
}

Bij drie elementen worden de indexen 2, 1 en 0 benaderd. Bij nul elementen wordt de lusinhoud nooit uitgevoerd. Neem <stdio.h> op voor printf.

Belangrijkste punten

  • Geef een arraylengte afzonderlijk door; uit een parameter kun je het aantal elementen niet afleiden.
  • Schrijven via een arrayparameter wijzigt de elementen van de aanroepende functie.
  • Gebruik const voor een parameter die elementen alleen leest.
  • De aanroepende functie stelt opslag beschikbaar voor meerdere uitvoerwaarden.
  • Tel bij gebruik van size_t terug vanaf n en benader i - 1.
2

Stel tekst voor met tekens en strings die eindigen op een nulteken

Tekst begint met tekencodes en eindigt met een opslagregel: een C-string stopt bij zijn eerste nulbyte. Leer hoe tekens, literals en beschrijfbare arrays verschillen en gebruik daarna lussen om tekst te tellen en te transformeren. UTF-8 voegt een wezenlijk onderscheid toe tussen een byte en een volledig codepunt. Door zowel de tekst als de opslag te volgen, zie je waarom een zichtbaar woord meer arrayposities kan innemen dan het aantal letters doet vermoeden.

2.1 Behandel tekens als numerieke codes

Een C-teken wordt opgeslagen als een numerieke code. Daardoor kan een programma een symbool afdrukken, het met een ander symbool vergelijken of met de code rekenen. De tekenset bepaalt welk getal elk symbool voorstelt.

Lees de code en het teken

Een char neemt één byte in: sizeof(char) is per definitie 1. Een tekenconstante zoals 'A' heeft het type int. In de hier gebruikte, op ASCII gebaseerde tekenset is de code 65:

printf("%c %d\n", 'A', 'A');

De uitvoer is “A 65”. %c toont het teken; %d toont het getal. ASCII kent de codes 0 tot en met 127 toe. Cijfers lopen van 48 tot en met 57, hoofdletters van 65 tot en met 90, kleine letters van 97 tot en met 122 en de spatie heeft code 32.

Enkele en dubbele aanhalingstekens maken verschillende dingen. 'A' is één tekencode. "A" is een array met 'A' en een afsluitende nulbyte. Sla één teken op met char c = 'A'; en vergelijk het met c == 'A'.

Schrijf tekens waarvoor een escapesequentie nodig is
C-schrijfwijzeTeken
'\n'Nieuwe regel, ASCII-code 10
'\t'Tab, ASCII-code 9
'\\'Backslash
'\''Enkel aanhalingsteken
'\0'Nulteken, code 0

Het nulteken verschilt van het zichtbare cijfer '0', dat ASCII-code 48 heeft. Een string gebruikt het nulteken om het einde aan te geven.

Zet codes om in bruikbare waarden

De C-standaard garandeert opeenvolgende cijfercodes. Nadat je hebt gecontroleerd of c een cijfer is, levert c - '0' de waarde ervan op. Zo is '7' - '0' gelijk aan 7, niet aan 55. Door de begincode af te trekken, haal je de positie van het cijfer binnen de tekenset weg.

ASCII-letters zijn ook opeenvolgend, dus door 'A' af te trekken zet je een hoofdletter om in een positie van 0 tot en met 25. Voor een Caesarverschuiving met drie gebruik je:

char shifted = (c - 'A' + 3) % 26 + 'A';

Voor 'X' geeft 65 aftrekken 23; drie optellen geeft 26; de rest na deling door 26 is nul; 65 optellen levert weer 'A' op. Alleen 'X' + 3 geeft code 91, oftewel '['. Rekenen met letters is afhankelijk van de lettervolgorde zoals in ASCII; C garandeert opeenvolgende cijfers, maar vereist geen opeenvolgende letters.

Classificeer en converteer met ctype.h

Neem <ctype.h> op voor isdigit, isalpha, isalnum, isspace, isupper, islower, toupper en tolower. De tests geven bij een overeenkomst een waarde anders dan nul terug en anders nul. toupper('q') geeft 'Q' terug; toupper('3') laat het cijfer ongewijzigd. isspace herkent scheidingstekens zoals spatie, tab en nieuweregelteken.

Een gewone char kan een type met teken zijn. Een byte boven 127 kan dan een negatieve waarde worden, die geen geldig argument is voor deze functies, tenzij het de speciale invoermarkering EOF is. Converteer eerst wanneer je bytes uit een string verwerkt:

if (isalpha((unsigned char) c)) {
    c = (char) toupper((unsigned char) c);
}

In de standaardlocale “C” herkennen deze functies de basisletters, geen volledige UTF-8-tekens die uit meerdere bytes bestaan. De expliciete typeconversie maakt de byte tot een geldig argument; ze decodeert geen UTF-8.

Belangrijkste punten

  • Tekenconstanten gebruiken enkele aanhalingstekens; stringliterals gebruiken dubbele aanhalingstekens.
  • \0 is een teken met waarde nul, niet het cijfer 0.
  • Trek '0' af van een cijferteken om de waarde ervan te krijgen.
  • Berekeningen met letterposities gebruiken hier de ASCII-volgorde.
  • Converteer een stringbyte naar unsigned char vóór een aanroep van een ctype-functie.
  • Een ctype-test geeft bij een overeenkomst een waarde anders dan nul terug, niet noodzakelijk 1.

2.2 Sla tekst op als strings die eindigen op een nulteken

Een C-string is een reeks tekens die eindigt op '\0'. Het afsluitende nulteken maakt deel uit van de opslag, maar niet van de tekstlengte. C heeft geen afzonderlijk ingebouwd stringtype: de tekens staan in een array.

Bekijk de bytes in de array
char word[] = "cat";
char name[20] = "Ada";

word bevat vier bytes: 'c', 'a', 't', '\0'. De arraygrootte is 4 en strlen(word) is 3. Neem <string.h> op voor strlen. De grotere array name bevat de drie letters, hun afsluitende nulteken en nog zestien nulbytes. De capaciteit is 20 en de huidige lengte is 3.

Een initialisator die de tekens één voor één opgeeft, kan dezelfde string maken:

char word[] = {'c', 'a', 't', '\0'};

Als je het laatste element weglaat, maak je drie tekens zonder afsluitend nulteken. C staat ook char word[3] = "cat"; toe. Daarmee vul je alle drie de posities en laat je de nulbyte weg. Zo’n array is geen C-string. Een functie zoals strlen of printf met %s blijft lezen tot ze een nulbyte tegenkomt en kan die array van drie bytes dus niet veilig als tekst verwerken.

Kies een wijzigbare array of een literal
DeclaratieWaar de tekst staatKunnen de tekens veranderen?
char a[] = "hello";Een kopie in array aJa
const char *p = "hello";Een stringliteral waarnaar p verwijstNee

a[0] = 'H'; verandert de kopie in “Hello”. Een literal mag niet worden gewijzigd. Zelfs als je die aan een gewone char * toewijst, is schrijven via die pointer ongedefinieerd gedrag. Met const char * maak je die beperking zichtbaar voor de compiler.

De compiler voegt stringliterals die naast elkaar staan samen: "Hello, " "world" geeft dezelfde tekst als "Hello, world". De resulterende string heeft nog steeds één afsluitend nulteken. Dit is handig om een lange literal over broncoderegels te verdelen.

Druk de tekst af en wijzig die
printf("%s\n", word);
printf("%c\n", word[0]);

De eerste aanroep drukt de hele string af. De tweede drukt het eerste teken af. Een stringliteral in printf staat tussen dubbele aanhalingstekens omdat het tekst is die een formaat bevat; %c ontvangt één tekencode.

Een toewijzing aan een element wijzigt één opgeslagen teken. Bij char name[20] = "Ada"; levert name[0] = 'E'; de string “Eda” op. Als je daarna name[1] = '\0'; instelt, ontstaat de string “E”. De bytes voor 'a' en het oude afsluitende nulteken bestaan later in de array nog steeds, maar stringbewerkingen stoppen bij de eerste nul.

Na de declaratie kan een array geen vervangende tekst ontvangen met name = "Bob";. Kopieer de tekens erin met een gecontroleerde aanroep van strcpy of met een lus, inclusief het afsluitende nulteken. De lengte van de vervangende tekst moet kleiner zijn dan de capaciteit.

Stel afwezigheid van tekst voor

De lege string "" neemt nog steeds één byte in voor '\0'. De lengte is nul en je kunt dit testen met name[0] == '\0'. Een lege string is een echte string met een afsluitend nulteken; een nulpointer verwijst daarentegen naar geen enkele stringopslag.

Belangrijkste punten

  • Een C-string eindigt bij zijn eerste '\0'.
  • De capaciteit omvat het afsluitende nulteken; de stringlengte niet.
  • Een tekenarray zonder afsluitend nulteken kun je niet als C-string doorgeven.
  • Kopieën van literals in arrays zijn wijzigbaar; literals zelf niet.
  • Een eerder afsluitend nulteken schrijven verkort de tekst zonder de grootte van de array te veranderen.

2.3 Verwerk strings teken voor teken

Het afsluitende nulteken van een string geeft een lus haar stoppunt. Bezoek de bytes ervoor om tekst te tellen, converteren of filteren. Bij het maken van een nieuwe string moet het programma zelf een nieuw afsluitend nulteken schrijven.

Doorloop de string één keer tot het einde
for (size_t i = 0; s[i] != '\0'; i++) {
    /* Process s[i]. */
}

Bij elke iteratie test de lus of de huidige byte bij de tekst hoort. Ze stopt vóór het afsluitende nulteken. Als je strlen(s) in die test aanroept, kan de hele string bij elke iteratie opnieuw worden doorlopen. Test de huidige byte rechtstreeks of bereken één keer size_t len = strlen(s); als de lengte gelijk blijft.

Gebruik voor omzetting naar hoofdletters een wijzigbare array en <ctype.h>:

for (size_t i = 0; s[i] != '\0'; i++) {
    s[i] = (char) toupper((unsigned char) s[i]);
}

“Hello, World 42” wordt “HELLO, WORLD 42”. De conversie behoudt de komma, spaties en cijfers. De conversie naar een type zonder teken maakt elke opgeslagen byte tot een geldig argument voor ctype.

Tel categorieën en woordbeginnen

Het oorspronkelijke “Hello, World 42” heeft 15 tekstbytes: 10 letters, twee cijfers, twee spaties en één komma. Afzonderlijke tellers kunnen isalpha, isdigit en isspace gebruiken. Bepaal voordat je gaat tellen wat een woord betekent. Voor een reeks letters begint een nieuw woord wanneer een letter op een niet-letter volgt of het eerste teken is:

int in_word = 0;
size_t words = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
    int letter = isalpha((unsigned char) s[i]) != 0;
    if (letter && !in_word) words++;
    in_word = letter;
}

Het voorbeeld heeft twee letterreeksen, “Hello” en “World”. De cijfers vormen volgens deze definitie geen woord. Door in_word bij elke iteratie bij te werken, kan een leesteken een reeks beëindigen.

Keer alleen de tekst om

Bepaal voor een string met tekens van één byte eerst één keer len en wissel dan s[i] met s[len - 1 - i] zolang i < len / 2. Bij het omkeren van “stressed” wissel je de eerste en laatste letter, dan het volgende paar, tot “desserts” ontstaat. De nulbyte blijft op index len staan. Als je een wissel bij s[len] begint, verplaats je die nul naar voren. Bytes op deze manier omkeren is geschikt voor ASCII-tekst; tekens van meerdere bytes moeten vóór het herschikken worden gedecodeerd.

Bouw een gefilterd resultaat op

Gebruik verschillende indexen voor lezen en schrijven, omdat sommige invoerbytes worden overgeslagen:

char in[] = "a1b2c3";
char out[sizeof in];
size_t j = 0;
for (size_t i = 0; in[i] != '\0'; i++) {
    if (isalpha((unsigned char) in[i])) {
        out[j++] = in[i];
    }
}
out[j] = '\0';

Het resultaat is “abc”. De doelarray heeft genoeg ruimte omdat filteren deze invoer niet langer kan maken. Zonder out[j] = '\0'; heeft de uitvoer geen vastgelegd stringeinde.

Verschuif voor een Caesarversleuteling hoofdletters ten opzichte van 'A' en kleine letters ten opzichte van 'a' en kopieer andere bytes ongewijzigd. Met sleutel 13 wordt “Hello, World!” omgezet in “Uryyb, Jbeyq!”. Als je die verschuiving herhaalt, krijg je het origineel terug omdat twee verschuivingen samen 26 zijn. Een vergelijking zoals s[i] == 'a' test één teken; gebruik strcmp om volledige tekst te vergelijken.

Belangrijkste punten

  • Test de huidige byte of bewaar de lengte om herhaald volledig doorzoeken te voorkomen.
  • Houd bij het filteren afzonderlijke lees- en schrijfindexen bij.
  • Schrijf een afsluitend nulteken na het opbouwen van een nieuwe string.
  • Keer tekstbytes om zonder het afsluitende nulteken te verplaatsen.
  • Definieer een woordgrens voordat je een woordteller schrijft.
  • Pas ASCII-letterverschuivingen afzonderlijk toe op hoofdletters en kleine letters.

2.4 Leg uit hoe UTF-8-tekst in char-arrays wordt opgeslagen

Tekstlengte kan een aantal bytes of een aantal Unicode-codepunten betekenen. Die aantallen verschillen vaak. Een C-char-array geeft toegang tot afzonderlijke bytes, dus de indexen en stringfuncties volgen niet automatisch volledige niet-ASCII-tekens.

Onderscheid tekens, codepunten en bytes

Een tekencodering beschrijft hoe tekst wordt opgeslagen. ASCII gebruikt de codes 0 tot en met 127 voor basisletters, cijfers, leestekens en besturingstekens. Unicode kent codepunten toe aan tekens uit veel schriftsystemen: “é” heeft codepunt U+00E9 en “€” heeft U+20AC. UTF-8 codeert scalaire Unicode-waarden met één tot vier bytes.

ASCII-codepunten behouden hun codering van één byte. De vooraf samengestelde “é” gebruikt twee bytes, 0xC3 en 0xA9; “€” gebruikt er drie. Veel afzonderlijke emoji-codepunten gebruiken vier bytes. Een weergegeven symbool kan meerdere codepunten bevatten, dus ook een aantal codepunten is niet altijd een aantal zichtbare symbolen.

Meet een UTF-8-string
char s[] = "café";
printf("%zu %zu\n", strlen(s), sizeof s);

Bij UTF-8-uitvoeringstekst drukt dit “5 6” af. De eerste drie letters gebruiken drie bytes en “é” gebruikt er twee, samen vijf tekstbytes. Het afsluitende nulteken voegt de zesde opslagbyte toe. s[3] is de eerste byte van “é”, niet de hele letter.

Als je na vier bytes afkapt, blijft 0xC3 staan maar verdwijnt de vervolgbyte 0xA9. Het resultaat zou geen geldige UTF-8 meer zijn. Een buffergrens in bytes vereist daarom een controle op tekengrenzen als het programma UTF-8-tekst afkapt.

Tel beginbytes in geldige UTF-8

Een vervolgbyte begint met de bits 10. De waarde ligt tussen 0x80 en 0xBF. Een beginbyte of ASCII-byte heeft dat patroon niet:

size_t points = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
    unsigned char c = (unsigned char) s[i];
    if ((c & 0xC0) != 0x80) points++;
}

Het masker 0xC0 behoudt alleen de twee hoogste bits; 0x80 stelt het patroon 10 op die posities voor. De test telt “café” als vier codepunten door de vier beginbytes te tellen. De test gaat uit van geldige UTF-8, valideert geen ongeldige reeksen en voegt geen codepunten samen tot zichtbare symbolen.

Kies bewerkingen die de codering behouden

Het kopiëren van een volledige UTF-8-string behoudt de bytes. Ook volledige strings samenvoegen, hun bytereeksen op gelijkheid vergelijken en zoeken naar een ASCII-teken of een volledige UTF-8-deelstring werken. UTF-8-vervolgbytes kunnen niet worden verward met ASCII-tekens of beginbytes.

strcmp ordent bytewaarden zonder teken, dus die volgorde verschilt van een taalspecifieke alfabetische volgorde. In UTF-8 hebben gewone Latijnse letters met accenten beginbytes boven ASCII 'z'. Een bytevergelijking plaatst die letters daarom na 'z', ongeacht woordenboekconventies.

In de standaardlocale “C” verwerken toupper en isalpha één byte en decoderen ze “é” niet als letter. Een expliciete typeconversie naar unsigned char maakt de aanroep geldig, maar maakt er geen Unicode-conversie tussen hoofdletters en kleine letters van. Daarvoor zijn functies voor brede tekens of een Unicode-bibliotheek nodig. Een Caesar-lus die alleen ASCII verwerkt, kan letters met accenten behouden door alleen bytes van A–Z of a–z te wijzigen.

De nulbyte is de UTF-8-codering van U+0000; andere gecodeerde codepunten bevatten geen nulbyte. Dat past bij de C-conventie voor afsluitende nultekens. UTF-16 kan nulbytes binnen gewone gecodeerde tekst plaatsen, dus een UTF-16-bytearray kan niet als een gewone char-string worden verwerkt.

Belangrijkste punten

  • strlen telt bytes, ook in UTF-8-tekst.
  • Eén Unicode-codepunt kan meerdere bytes innemen.
  • Een char-index kan binnen een gecodeerd teken uitkomen.
  • Bytes tellen die geen vervolgbyte zijn, telt alleen codepunten bij geldige UTF-8.
  • Bytegewijze gelijkheid werkt voor identiek gecodeerde tekst; bytevolgorde is geen woordenboekvolgorde.
  • ctype-aanroepen die één byte verwerken, decoderen geen UTF-8.
3

Gebruik de standaardfuncties voor strings

De standaardbibliotheek biedt veelgebruikte tekstbewerkingen, maar elke functie verwacht een bepaald soort invoer en geeft een specifieke betekenis aan het argument voor de grootte. Leer strings te meten, kopiëren, uitbreiden, vergelijken en doorzoeken, en kies daarna waar passend voor begrensde opmaak of bytebewerkingen. Door de capaciteit te controleren en retourwaarden te interpreteren, maak je van een korte functieaanroep een voorspelbare bewerking op bekende opslagruimte.

3.1 Bepaal de lengte van strings, kopieer ze en voeg ze samen

De functies in <string.h> werken met tekst die eindigt op een nulteken. Ze kunnen de lengte bepalen, de tekst kopiëren of meer tekst toevoegen, maar kopieerfuncties achterhalen de capaciteit van de doelarray niet. Bepaal de benodigde opslagruimte voordat je schrijft.

Meet de lengte zonder het afsluitende nulteken

strlen(s) geeft een size_t terug met het aantal bytes vóór de eerste nulbyte. Zo is strlen("hello") gelijk aan 5 en strlen("") gelijk aan nul. Druk het resultaat af met %zu.

char buf[64] = "hi";
printf("%zu %zu\n", strlen(buf), sizeof buf);

Dit drukt “2 64” af. Het eerste resultaat meet de huidige tekst; het tweede meet de beschikbare opslagruimte in de array. Een latere kopieerbewerking kan het eerste veranderen zonder het tweede te veranderen. Beide aanroepen zijn hier nuttig omdat buf een echte array is en al een geldige string bevat.

Kopieer een volledige string

strcpy(dest, src) kopieert tekst en de afsluitende nulbyte. De benodigde capaciteit is de lengte van de bron plus één:

char dest[12];
const char *src = "Maple";
if (strlen(src) < sizeof dest) {
    strcpy(dest, src);
}

“Maple” gebruikt vijf tekstbytes en één afsluitend nulteken, dus past de tekst. De strikte < is belangrijk: als de lengte van de bron gelijk is aan de capaciteit, blijft er geen positie over voor de nulbyte. Als de controle mislukt, moet het programma de fout afhandelen voordat het de doelarray als nieuwe string gebruikt.

Een arraytoewijzing zoals dest = src; kan de inhoud niet vervangen. De ene char *-variabele aan de andere toewijzen is toegestaan, maar kopieert alleen een verwijzing naar dezelfde opslag. Er ontstaat geen extra kopie van de tekens.

Voeg tekst toe aan een bestaande string

strcat(dest, src) zoekt eerst het afsluitende nulteken dat al in dest staat. De functie vervangt dat nulteken door de eerste byte van de bron en schrijft een nieuw afsluitend nulteken na de toegevoegde tekst. De capaciteit van de doelarray moet voldoende zijn voor beide huidige lengtes plus één.

char full[16];
strcpy(full, "Ada");
strcat(full, " ");
strcat(full, "Lovelace");

De lengte groeit van 3 naar 4 naar 12. Het uiteindelijke “Ada Lovelace” heeft 13 bytes opslagruimte nodig, waardoor drie bytes ongebruikt blijven. Een doelarray van 12 bytes zou één byte te klein zijn, hoewel de zichtbare tekst even lang is.

Voeg nooit tekst toe aan een niet-geïnitialiseerde lokale buffer. Bij char out[20]; strcat(out, "hi"); heeft strcat geen vastgelegd teksteinde om te vinden. Begin met char out[20] = "";, schrijf out[0] = '\0'; of maak van de eerste bewerking een gecontroleerde kopieerbewerking.

Houd rekening met opslag en doorzoektijd

Bron en doel mogen bij strcpy of strcat niet overlappen. Als je tekst binnen dezelfde buffer verplaatst, kan overlap ontstaan, ook als beide locaties afzonderlijk geldig zijn. memmove is de functie die bedoeld is voor overlappende bytegebieden.

strlen moet tot het afsluitende nulteken doorzoeken. strcat moet de huidige doelstring doorzoeken voordat de functie tekst toevoegt. Herhaald toevoegen in een lus doorzoekt dus opnieuw tekst die al is opgebouwd. Als je veel stukken samenvoegt, houd dan de huidige uitvoerlengte bij en schrijf elk stuk op die index. Controleer daarbij de resterende capaciteit en zorg dat het afsluitende nulteken aanwezig blijft. Een bekende lengte is alleen bruikbaar zolang die nog overeenkomt met de werkelijke inhoud.

Belangrijkste punten

  • strlen meet de huidige tekst; sizeof op een array meet de capaciteit ervan.
  • Een kopie heeft de bronlengte plus één byte nodig.
  • Toevoegen vereist beide stringlengtes plus één byte.
  • Initialiseer de doelarray als string voordat je tekst toevoegt.
  • Kopiëren of toevoegen met overlappende stringgebieden is ongeldig.

3.2 Vergelijk en doorzoek strings

Stringvergelijking beantwoordt vragen over tekst, terwijl zoeken in een string een plaats binnen die tekst vindt. Geen van beide taken voer je uit door arraynamen met == te vergelijken. Gebruik het bibliotheekresultaat volgens wat het voorstelt: het teken van een vergelijkingsresultaat, een lengte of een locatie van een overeenkomst.

Vergelijk tekst en beginstukken

strcmp(a, b) vergelijkt overeenkomstige bytes als unsigned char-waarden tot er een verschil is of het einde van een string wordt bereikt. Het resultaat is nul bij gelijke tekst, negatief als a eerder komt en positief als die later komt. Alleen het teken is vastgelegd; verwacht niet precies -1 of 1.

if (strcmp(command, "quit") == 0) {
    puts("Goodbye");
}

De expliciete test op nul betekent dat de tekst gelijk is. Een gewone if (strcmp(command, "quit")) voert het blok uit bij ongelijke tekst, omdat een waarde anders dan nul waar is. De vergelijking command == "quit" zou opslaglocaties vergelijken.

In ASCII-volgorde komt “apple” bij de eerste letter vóór “banana”. “Zebra” komt vóór “apple” omdat hoofdletter 'Z' code 90 heeft en kleine letter 'a' code 97. “app” komt vóór “apple” omdat de nulbyte van de eerste string wordt bereikt vóór de 'l' van de andere. Dit is ordening op bytes, geen woordenboekvergelijking.

strncmp(a, b, n) vergelijkt hoogstens n bytes. strncmp(s, "--", 2) == 0 herkent twee streepjes aan het begin, maar betekent niet dat de hele string alleen “--” is. Standaard-C heeft geen functie om hele strings te vergelijken zonder onderscheid tussen hoofdletters en kleine letters. Een programma kan zelf geconverteerde bytes vergelijken; POSIX biedt strcasecmp in <strings.h>.

Zoek een locatie

Gebruik strchr voor de eerste keer dat één teken voorkomt, strrchr voor de laatste keer en strstr voor de eerste volledige deelstring. Ze geven een pointer naar de locatie van de overeenkomst terug, of NULL als er geen overeenkomst is.

char name[] = "report.final.txt";
char *extension = strrchr(name, '.');
if (extension != NULL) {
    printf("%s\n", extension);
}

Dit drukt “.txt” af, inclusief de gevonden punt. Als je met strchr bij de eerste punt zou beginnen, zou “.final.txt” worden afgedrukt. strstr(name, "final") vindt het achterste deel dat begint met “final.txt”; zoeken naar “draft” geeft NULL. Controleer op NULL voordat je een zoekresultaat afdrukt of leest.

Zoek een scheidingsteken of splits tekst

strcspn(s, set) telt de bytes aan het begin die bij geen van de tekens in set horen. Bij "oak:pine,birch" en de verzameling ":," stopt de functie bij de dubbele punt. Voor een geldige invoerstring verwijdert dit patroon het eerste nieuweregelteken:

buf[strcspn(buf, "\n")] = '\0';

Als er geen nieuweregelteken is, is de teruggegeven index de stringlengte en overschrijft de toewijzing dus het afsluitende nulteken dat daar al staat.

strtok splitst een wijzigbare string met elk teken uit een verzameling scheidingstekens:

char colors[] = "red, green,blue";
char *token = strtok(colors, " ,");
while (token != NULL) {
    puts(token);
    token = strtok(NULL, " ,");
}

De tokens zijn “red”, “green” en “blue”. De eerste aanroep geeft de array door; latere aanroepen geven NULL door om verder te gaan. De functie schrijft nulbytes in de invoer en bewaart een verborgen toestand tussen aanroepen. Gebruik beschrijfbare opslag en verwacht niet dat de oorspronkelijke string met scheidingstekens intact blijft. Opeenvolgende scheidingstekens leveren geen lege tokens op.

Belangrijkste punten

  • Gebruik strcmp(a, b) == 0 voor gelijke tekst.
  • Interpreteer een vergelijkingsresultaat aan de hand van het teken, niet van de exacte grootte.
  • Overeenstemming van een beginstuk bewijst niet dat de hele strings gelijk zijn.
  • Controleer zoekresultaten op een nulpointer.
  • strcspn geeft de lengte van een beginstuk terug; strtok wijzigt de invoer en onthoudt de positie.
  • De ordening van strings volgt bytecodes zonder teken.

3.3 Kopieer binnen de grenzen en werk rechtstreeks met geheugenbytes

Een lengtegrens is alleen nuttig als die de juiste grootheid beschrijft. Sommige functies begrenzen bronbytes, andere de capaciteit van de doelarray, en geheugenfuncties verwerken ruwe bytes zonder stringeinden. Lees het contract van elke functie voordat je de grens kiest.

Weet wat n betekent

strncpy(dest, src, n) schrijft precies n posities als de bron korter is en vult aan met nulbytes. Als de bron minstens n bytes tekst heeft, kopieert de functie die bytes zonder afsluitend nulteken. Voor een array met een positieve capaciteit maak je zo een afgekorte kopie met afsluitend nulteken:

strncpy(dest, src, sizeof dest - 1);
dest[sizeof dest - 1] = '\0';

De laatste toewijzing is essentieel wanneer de bron de grens vult. Een naam of opdracht afkappen kan nog steeds de betekenis veranderen, dus wijs te lange invoer af als een volledige kopie vereist is.

strncat(dest, src, n) voegt hoogstens n tekstbytes toe en daarna een afsluitend nulteken. De functie ontvangt niet de totale capaciteit. Voor een geldige string die al in een doelarray staat, is de resterende ruimte voor tekst sizeof dest - strlen(dest) - 1. Als je sizeof dest als n doorgeeft, kan de doelarray overlopen.

Maak tekst op en detecteer afkapping

snprintf, gedeclareerd in <stdio.h>, ontvangt de totale capaciteit van de doelarray. Bij een capaciteit groter dan nul schrijft de functie hoogstens één tekstbyte minder en daarna een afsluitend nulteken:

char buf[8];
int needed = snprintf(buf, sizeof buf, "%s-%d", "item", 2026);
if (needed < 0) {
    puts("Formatting error");
} else if ((size_t) needed >= sizeof buf) {
    puts("Text did not fit");
}

Het volledige resultaat “item-2026” heeft negen tekstbytes. De buffer slaat “item-20” en de nulbyte op, terwijl needed gelijk is aan 9. De retourwaarde telt de tekst die zou zijn geproduceerd, zonder het afsluitende nulteken. Controleer of het resultaat negatief is voordat je het naar size_t converteert.

Kopieer en vul bytes

memcpy(dest, src, n) kopieert precies n bytes, inclusief eventuele nulbytes, zonder naar een afsluitend nulteken te zoeken. Voor twee even grote arrays van gehele getallen kopieert memcpy(b, a, sizeof a) de volledige opslag als de gebieden niet overlappen. Waar elk van vijf gehele getallen vier bytes inneemt, is dat aantal 20 bytes. memmove kopieert bytes met gedefinieerd gedrag voor overlappende gebieden.

memset(a, 0, sizeof a) vult elke byte met nul en zet een array van gehele getallen op nul. Het tweede argument beschrijft elke byte, niet elk element. Bij gehele getallen van vier bytes maakt vullen met 1 de bytes 01 01 01 01, wat bij de gebruikelijke representatie 16.843.009 oplevert, niet het gehele getal 1. Gebruik een lus om geheeltallige waarden anders dan nul toe te wijzen.

memcmp(a, b, n) vergelijkt opgeslagen bytes. Nul betekent dat die bytereeksen overeenkomen. Vergelijk elementen voor waardegelijkheid: de drijvendekommawaarden 0.0 en -0.0 zijn bij vergelijking gelijk, terwijl hun representaties kunnen verschillen.

Herken andere bibliotheekfamilies

De BSD-functies strlcpy en strlcat zijn in 2024 aan POSIX toegevoegd, maar zijn geen kernfuncties van C17. Ze geven een lengte terug die ze probeerden te maken; een resultaat dat minstens zo groot is als de opgegeven grootte duidt op afkapping. strlcpy schrijft een afsluitend nulteken als de grootte positief is. strlcat doet dat alleen als de bestaande tekst binnen die grootte eindigt. Ze zijn beschikbaar in glibc 2.38 en later. Optionele functies uit Annex K, zoals strcpy_s, ontbreken in veel bibliotheken, waaronder glibc.

Belangrijkste punten

  • strncpy kan het afsluitende nulteken weglaten; strncat heeft ruimte nodig voor een nieuw nulteken.
  • snprintf geeft de volledige benodigde tekstlengte terug, of een negatieve waarde bij een fout.
  • Een niet-negatief opmaakresultaat dat minstens zo groot is als de capaciteit betekent afkapping.
  • Geheugenfuncties tellen bytes en stoppen niet bij nul.
  • Gebruik memmove voor overlappende gebieden.
  • memset vult bytes; memcmp vergelijkt representaties.
4

Orden gegevens in meerdimensionale arrays

Tabellen ordenen waarden op basis van meer dan één positie. Een C-tabel slaat rijen van gelijke breedte achter elkaar op en met geneste lussen kan een programma per rij of per kolom werken. Leer hoe je die vorm declareert, aan een functie doorgeeft en beide indexen geldig houdt. Lijsten met tekst brengen een nuttige ontwerpkeuze mee: beschrijfbare tekenrijen reserveren vaste ruimte, terwijl arrays van pointers verwijzen naar strings die elders staan.

4.1 Declareer en doorloop tweedimensionale arrays

Een tabel heeft een rij-index en een kolomindex nodig. In C is een tweedimensionale array een array waarvan de elementen zelf arrays zijn. Elke rij heeft evenveel elementen en elke index heeft een eigen grens.

Declareer rijen en kolommen
int grid[3][4];
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};

grid bevat drie rijen met elk vier gehele getallen: in totaal twaalf gehele getallen. De rij-indexen lopen van 0 tot en met 2; de kolomindexen van 0 tot en met 3. grid[1][2] benadert de derde waarde in de tweede rij.

Geneste accolades in de initialisator laten de indeling in rijen zien. Ontbrekende initialisatiewaarden worden nul. Je kunt de compiler de rijen laten tellen in int m[][3] = {{1, 2, 3}, {4, 5, 6}};, maar het aantal kolommen moet je opgeven. Zonder dat aantal kan de compiler niet bepalen waar elke rij eindigt.

Volg de rijgewijze opslag

C slaat een volledige rij op voordat de volgende begint. In grid vormen de eerste vier opgeslagen gehele getallen rij 0, de volgende vier rij 1 en de laatste vier rij 2. grid[1][2] is het zevende gehele getal vanaf het begin, op elementpositie 6 bij indexering vanaf nul: één volledige rij van vier, gevolgd door nog twee elementen.

Waar gehele getallen vier bytes innemen, gebruikt één rij zestien bytes en de hele tabel achtenveertig. Op de plaats van de declaratie:

size_t rows = sizeof grid / sizeof grid[0];
size_t cols = sizeof grid[0] / sizeof grid[0][0];

De eerste deling geeft 3 en de tweede 4. Een rij is zelf een array; daarom meet sizeof grid[0] meer dan één geheel getal.

Doorloop de tabel en bereken totalen
int sales[2][3] = {{4, 0, 7}, {5, 2, 1}};
int total = 0;
for (int r = 0; r < 2; r++) {
    int row_sum = 0;
    for (int c = 0; c < 3; c++) {
        row_sum += sales[r][c];
    }
    printf("%d\n", row_sum);
    total += row_sum;
}

De eerste rij levert 11 op en de tweede 8. Als je die rijtotalen optelt, krijg je 19. Door row_sum binnen de buitenste lus opnieuw in te stellen, begint voor elke rij een nieuw totaal. Als de variabele slechts één keer vóór beide lussen was geïnitialiseerd, zou de tweede afgedrukte waarde ook de eerste rij bevatten.

Voor kolomtotalen zet je de kolomlus buiten en de rijlus binnen, terwijl je nog steeds sales[r][c] gebruikt. De drie totalen worden 9, 2 en 8, samen opnieuw 19. Als je de doorloopvolgorde verandert, hoef je de betekenis van de twee indexen niet te veranderen. Een doorloop met de rijen als buitenste lus volgt de aaneengesloten opslag en verbetert bij grote tabellen vaak het gebruik van de cache.

Houd elke dimensie binnen de grenzen

grid[c][r] verwisselt de betekenis van de indexen. Als c 3 bereikt, vraagt de expressie een vierde rij op die niet bestaat. Dat het volledige object twaalf gehele getallen bevat, betekent niet dat je met een kolomindex de grens van één rij mag overschrijden.

Schrijf twee afzonderlijke indexbewerkingen: grid[1][2]. In C17 evalueert grid[1, 2] de komma-expressie en selecteert grid[2], een rij, in plaats van één element. Bij meer dimensies pas je hetzelfde idee herhaaldelijk toe: int cube[2][3][4] heeft 24 geheeltallige elementen, die je met drie indexen selecteert.

Belangrijkste punten

  • Een tweedimensionale array is een array van rijen met gelijke breedte.
  • Rijen worden na elkaar opgeslagen in rijgewijze opslagvolgorde.
  • Houd de grenzen van rijen en kolommen gescheiden.
  • Stel een accumulator voor een rij of kolom opnieuw in wanneer je aan een nieuwe groep begint.
  • Alleen de eerste dimensie kan uit een initialisator worden afgeleid.

4.2 Geef tabellen door aan functies en sla lijsten met strings op

Een functie die een tabel ontvangt, heeft genoeg informatie nodig om elke rij te vinden. Een lijst met strings brengt nog een keuze mee: sla de tekens op in rijen van gelijke breedte of sla verwijzingen op naar strings met verschillende lengtes.

Geef een tabelparameter zijn rijbreedte
void double_grid(int rows, int g[][4]) {
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < 4; c++) {
            g[r][c] *= 2;
        }
    }
}

De eerste dimensie kan worden weggelaten omdat het aantal rijen afzonderlijk wordt doorgegeven. De breedte 4 bepaalt het type van elke rij. Zonder die breedte zou de afstand tussen rijen voor g[r][c] niet bekend zijn. Een declaratie met int g[][] is ongeldig. Wijzigingen via deze parameter blijven bestaan in de tabel van de aanroepende functie; een tabelparameter die alleen wordt gelezen, kan const int g[][4] gebruiken.

Sinds C99 accepteren compilers die parameters met een variabel gemodificeerd type ondersteunen dimensies die eerder in de parameterlijst zijn gedeclareerd:

void print_grid(int rows, int cols, int g[rows][cols]);

GCC en Clang ondersteunen deze C17-vorm. Hiermee kan één functie verschillende rijbreedtes ontvangen; geef positieve dimensies door die overeenkomen met de werkelijke tabel. C23 vereist ondersteuning voor deze parametertypen, terwijl automatische objecten die arrays met variabele lengte zijn een afzonderlijke optionele functie blijven. Parameters met vaste breedte zijn nuttig als de compilerondersteuning beperkt is.

Sla strings op in rijen
char days[7][10] = {
    "Monday", "Tuesday", "Wednesday", "Thursday",
    "Friday", "Saturday", "Sunday"
};

Elke rij heeft opslagruimte voor tien tekens, ongeacht de huidige tekstlengte. “Wednesday” heeft alle tien posities nodig: negen letters en een afsluitend nulteken. days[2] selecteert die hele rij als string; days[2][0] selecteert 'W'.

Je kunt een rij afdrukken met %s, meten met strlen of vergelijken met strcmp. De rij kan ook een gecontroleerde kopie ontvangen. strcpy(days[0], "Mon"); past in tien bytes en wijzigt de tekst van rij 0. De rij blijft tien bytes breed en de volgende rij blijft op dezelfde locatie. Vervangende tekst van tien bytes zou elf bytes nodig hebben en niet passen.

Sla verwijzingen naar literals op
const char *names[] = {"Ann", "Bartholomew", "Li"};

Dit declareert een array van pointers. Elk element verwijst naar een literal met zijn eigen lengte plus een afsluitend nulteken. names[1] verwijst naar “Bartholomew” en names[1][0] is 'B'. De literals mogen niet worden gewijzigd.

De pointerelementen kunnen veranderen, hoewel de tekens van de literals dat niet kunnen. names[0] = "Anna"; verandert naar welke literal het eerste element verwijst; de instructie verlengt “Ann” niet op dezelfde locatie. In de vorm met 2D-rijen is een element een tekenarray met vaste breedte en kan het niet op die manier opnieuw worden toegewezen. Kies beschrijfbare rijen voor tekst die je op dezelfde locatie bewerkt en verwijzingen naar literals voor een lijst met vaste tekstkeuzes. De opdrachtregelparameter argv heeft de vorm van een array van pointers, hoewel de meegegeven argumenttekens wijzigbaar zijn.

Belangrijkste punten

  • Tabelparameters hebben elke dimensie na de eerste nodig.
  • Een afzonderlijk doorgegeven dimensie moet overeenkomen met de werkelijke tabel.
  • Elke stringrij heeft binnen zijn vaste breedte een eigen afsluitend nulteken nodig.
  • Een array van pointers slaat verwijzingen op, geen tekenrijen met gelijke breedte.
5

Verwerk opdrachtregelargumenten

Een opdrachtregel voorziet een programma bij het starten van tekst. Met aantallen argumenten, groepering door de shell en stringbewerkingen kan het programma die tekst interpreteren zonder aan te nemen dat een argument bestaat of al een getal bevat. Volg een gecontroleerd pad van argv naar een bruikbare waarde: controleer of het argument aanwezig is, converteer de volledige tekst, detecteer conversiefouten en dwing het bereik af dat het programma daadwerkelijk accepteert.

5.1 Lees argc en argv

Met opdrachtregelargumenten kan de aanroepende partij bij het starten van een programma tekst meegeven. De besturingsomgeving maakt die strings beschikbaar voor main. Controleer hoeveel argumenten zijn aangekomen voordat je ze met een index benadert.

Lees de argumentenlijst
int main(int argc, char *argv[]) {
    for (int i = 1; i < argc; i++) {
        printf("%d: %s\n", i, argv[i]);
    }
    return 0;
}

Neem <stdio.h> op voor de uitvoer. argc is het aantal argumenten en argv is de lijst met strings. De gelijkwaardige schrijfwijze char **argv voor de parameter beschrijft dezelfde interface. De lus begint bij 1 omdat index 0 normaal de naam bevat waarmee het programma is gestart.

In een gewone shell levert ./greet Ada Lovelace drie strings op: “./greet”, “Ada” en “Lovelace”. Dus argc is 3 en het laatste gebruikersargument is argv[2]. Op argv[argc], hier argv[3], staat een nulpointer; die is een stopwaarde na de eigenlijke argumentstrings.

Laat de shell tekst groeperen

Aanhalingstekens beïnvloeden hoe de shell argumenten opbouwt. Als je ./greet "Ada Lovelace" uitvoert, geef je één gebruikersargument met een spatie mee. De aanhalingstekens groeperen de woorden en worden geen onderdeel van de argumenttekst. argc is nu 2.

Unix-achtige shells kunnen ook patronen uitbreiden voordat het programma start. Bij ./count *.txt ontvangt het programma doorgaans de namen die de shell vindt als afzonderlijke argumenten. Het ontvangt de resulterende strings, geen verzoek om het patroon zelf uit te breiden. Het precieze uitbreidingsgedrag hangt af van de shell en de instellingen ervan.

Wijs een ontbrekend of extra argument af

Als een programma precies één gebruikersargument nodig heeft, vereis dan argc == 2 voordat je het gebruikt:

if (argc != 2) {
    printf("Usage: %s name\n", argv[0]);
    return 1;
}
printf("Hello, %s\n", argv[1]);

Als argc gelijk is aan 1, is argv[1] de nulpointer die als stopwaarde dient. Je kunt die niet aan strlen doorgeven of met %s afdrukken. Indexen voorbij de stopwaarde liggen buiten de argumentarray.

Nul teruggeven vanuit main geeft aan dat het programma is geslaagd. Een status anders dan nul, zoals de 1 hierboven, laat de shell weten dat het programma niet succesvol is voltooid. <stdlib.h> biedt ook EXIT_SUCCESS en EXIT_FAILURE.

Behandel elk argument als een string

argv[i][0] benadert het eerste teken van het argument; strlen(argv[i]) meet de tekstbytes; strcmp(argv[i], "-v") == 0 controleert een volledige optie. Een argument dat eruitziet als een getal is nog steeds tekst en moet vóór een berekening worden geconverteerd.

argv[0] legt meestal vast hoe het programma is gestart, maar kan leeg zijn als die informatie niet beschikbaar is. Het is nuttig voor berichten, niet als vaste identiteitscontrole. Argumenttekens mogen tijdens het programma worden gewijzigd, maar de meegeleverde opslag biedt geen grond om aan te nemen dat een langere vervangende tekst past. Om een extensie toe te voegen of tekst op een andere manier langer te maken, kopieer je die eerst naar een afzonderlijke buffer met een passende grootte.

Belangrijkste punten

  • argc telt de meegegeven argumentstrings, inclusief de gebruikelijke vermelding van de startnaam.
  • Controleer het aantal argumenten voordat je een gebruikersargument leest.
  • argv[argc] is een nulpointer, geen lege argumentstring.
  • Aanhalingstekens in de shell kunnen spaties binnen één argument houden.
  • Gebruik stringbewerkingen voor argumenttekst en conversie voor getallen.

5.2 Converteer en valideer numerieke argumenten

Een numeriek argument komt binnen als tekens, niet als geheel getal. Een betrouwbare conversie controleert drie afzonderlijke vragen: vormden cijfers een getal, gebruikte de conversie de vereiste tekst en is de resulterende waarde toegestaan?

Voorkom verlies van foutinformatie

atoi, gedeclareerd in <stdlib.h>, voert een eenvoudige conversie uit. De functie zet “13” om in 13, maar ook “12abc” in 12. Ze geeft zowel voor “0” als voor tekst zoals “abc” die niet met een getal begint nul terug. Een resultaat buiten het waardenbereik is ongedefinieerd gedrag, dus een test op alleen cijfers gevolgd door atoi is bij zeer lange invoer nog steeds onveilig.

Een vormregel zoals “alleen één of meer cijfers” vereist een afzonderlijke controle. Wijs een lege string af en vereis dat elke byte voldoet aan isdigit((unsigned char) c). Die regel accepteert “13” en wijst “13a”, “-5” en “” af. De regel controleert de schrijfwijze, niet of de waarde in een geheel getal van de machine past.

Gebruik strtol en bekijk waar de conversie stopte
#include <errno.h>
#include <stdlib.h>

char *end;
errno = 0;
long count = strtol(argv[1], &end, 10);
if (end == argv[1] || *end != '\0' || errno == ERANGE) {
    puts("Enter a whole number");
    return 1;
}
if (count < 1 || count > 100) {
    puts("Count must be from 1 to 100");
    return 1;
}

Dit fragment volgt op een controle van het aantal argumenten en gebruikt <stdio.h> voor puts. De 10 kiest decimale conversie. end ontvangt de locatie waar de conversie stopte; met &end kan de functie die locatie opslaan. *end leest het teken op die locatie. Pointerrekenen is niet nodig.

Voor “13a” is de teruggegeven waarde 13, maar is *end gelijk aan 'a', dus wordt de tekst afgewezen. Voor een lege string of “abc” is end gelijk aan het begin van het argument. Dat laat zien dat geen getal is verwerkt. Beide controles zijn nodig: alleen op de afsluitende nul testen zou een lege string ten onrechte accepteren.

Zet vóór elke conversie errno terug op nul. Een waarde buiten het bereik van long zorgt ervoor dat strtol een grenswaarde teruggeeft en ERANGE instelt. Zonder die reset kan een oude fout bij deze conversie lijken te horen. Controleer het resultaat voordat je het naar int vernauwt; gebruik INT_MIN en INT_MAX uit <limits.h> als het volledige bereik van int het toegestane bereik is.

Pas de eigen regel van het programma toe

strtol accepteert witruimte aan het begin en een optioneel teken. Bepaal of die vormen zijn toegestaan. Als de interface alleen cijfers vereist, voer dan naast de conversiecontroles ook de controle op een niet-lege reeks cijfers uit. Controleer na een geslaagde conversie het toegestane waardenbereik van de toepassing voordat je een waarde als aantal of index gebruikt. “0” kan volkomen geldige numerieke tekst zijn en toch ongeldig zijn voor een aantal dat positief moet zijn.

Een Caesarprogramma dat een niet-negatieve sleutel accepteert, controleert eerst argc, dan of de invoer alleen cijfers bevat en daarna het volledige resultaat van strtol en het bereik. Door een geldige sleutel met % 26 te reduceren, krijg je vóór de letterberekening een verschuiving van 0 tot en met 25. Sleutel 27 heeft het effect van sleutel 1. Voer de bereikcontrole uit vóór een expliciete typeconversie naar een smaller type of een berekening die kan overlopen. Voor decimale waarden biedt strtod de bijbehorende interface voor eindpositie en bereikfout.

Belangrijkste punten

  • Argumenten die eruitzien als getallen moeten nog steeds worden geconverteerd.
  • atoi kan geldige nul niet onderscheiden van een mislukte conversie.
  • Controleer of strtol iets heeft gelezen en alle vereiste tekst heeft verwerkt.
  • Zet errno terug, controleer ERANGE en controleer daarna het toegestane waardenbereik van de toepassing.
  • Een controle op alleen cijfers sluit overloop niet uit.
  • Valideer een waarde voordat je die naar een smaller type converteert of als index gebruikt.
6

Voorkom toegang buiten de grenzen en bufferoverloop

Grensfouten verbinden verschillende eerdere ideeën: een lus kan een extra iteratie uitvoeren, tekst kan een extra byte nodig hebben en een functie kan de capaciteit van een array uit het oog verliezen. Leer de ongeldige toegang te herkennen voordat je de gevolgen voorspelt. Lees en kopieer invoer daarna binnen bekende grenzen en controleer afzonderlijk of de bewerking is geslaagd en of de invoer volledig is. Zo bevat een buffer waarin de gegevens passen ook de gegevens die het programma bedoelde te accepteren.

6.1 Herken toegang buiten de grenzen en de gevolgen daarvan

Elke toegang tot een array vereist een geldige index. C vereist tijdens de uitvoering geen grenscontrole, dus moet de programmeur de index binnen het object houden. Toegang buiten het bereik heeft ongedefinieerd gedrag: de taal garandeert geen resultaat.

Herken ongeldige toegang

Voor int a[5] zijn de geldige indexen 0 tot en met 4. Index 5 is al te groot en -1 is negatief. Een compiler kan een zichtbare fout melden, maar veel ongeldige indexen hangen af van invoer tijdens de uitvoering en worden niet tijdens het compileren afgewezen.

Een programma met een fout kan goed lijken te werken, vastlopen of onverwachte waarden opleveren. Een schrijfbewerking kan nabijgelegen gegevens aantasten; het verschijnsel kan pas later in een niet-gerelateerde berekening optreden. Een leesbewerking kan bytes buiten het bedoelde object benaderen. Geen van beide bewerkingen heeft een betrouwbaar resultaat. Herhaling van een geslaagde uitvoering kan niet vaststellen dat de toegang geldig is.

Tel elke schrijfbewerking, inclusief het afsluitende nulteken
char name[8];
strcpy(name, "Christopher");

“Christopher” heeft elf tekstbytes. Bij het kopiëren wordt ook het afsluitende nulteken geschreven, dus probeert deze aanroep twaalf bytes in acht posities te plaatsen. Dat is een bufferoverloop. Op echte systemen kan een overloop andere gegevens of besturingsinformatie beschadigen. In het verleden hebben aanvallen, waaronder die van de Morris-worm uit 1988, zulke fouten in de invoerverwerking uitgebuit.

Een overloop kan slechts één byte zijn:

char s[5];
for (int i = 0; i <= 5; i++) {
    s[i] = 'x';
}

De lusinhoud wordt zes keer uitgevoerd en schrijft naar index 5. Als je de test verandert in i < 5, blijven vijf schrijfbewerkingen voor tekens binnen de array, maar ontstaat nog steeds geen string. Schrijf voor een string van vier 'x'-tekens alleen naar de indexen 0 tot en met 3 en sla daarna '\0' op bij index 4.

Valideer berekende indexen

Een frequentietabel kan een teken omzetten in een index, maar aftrekken alleen garandeert geen geldig bereik:

int counts[26] = {0};
if (c >= 'a' && c <= 'z') {
    counts[c - 'a']++;
}

Bij ASCII-volgorde beperkt die test het resultaat tot 0 tot en met 25. Voor 'A' of een byte met een leesteken kan c - 'a' zonder de test een negatieve index opleveren. Test de oorspronkelijke waarde voordat je een index gebruikt; controleren na het schrijven is te laat.

Valideer bij een tabel de rij en kolom afzonderlijk. In int g[2][3] is een kolomindex van 3 ongeldig, ook als de volgende rij ernaast in het geheugen staat. Gebruik de opslagruimte van de volledige tabel niet als reden om de grens van een rij te overschrijden.

Gebruik controles die bij de bewerking passen

Een arraylus gebruikt een strikte bovengrens; bij toevoegen controleer je het aantal tegenover de capaciteit; bij een string kopiëren controleer je de tekstlengte plus één. Functies ontvangen werkelijke lengtes of capaciteiten van hun aanroepende functies. Deze controles gaan over verschillende grootheden, dus één vast in de code opgenomen grootte kan ze niet allemaal vervangen.

Compileer met waarschuwingen zoals -Wall -Wextra. Met -fsanitize=address -g kunnen GCC en Clang veel ongeldige toegangen op uitgevoerde paden melden en de bronlocatie tonen. Zulke tests helpen fouten te vinden, maar over niet-uitgevoerde paden moet je nog steeds redeneren om de grenzen te controleren. Beoordeel elke lees- en schrijfbewerking met een index door het object, de grens en de voorwaarde die de index geldig houdt te bepalen.

Belangrijkste punten

  • Lezen en schrijven buiten de grenzen heeft ongedefinieerd gedrag.
  • Een geslaagde uitvoering is geen bewijs dat een index geldig is.
  • Bij een gekopieerde string telt het afsluitende nulteken mee in het aantal geschreven bytes.
  • Valideer gegevens voordat je er een array-index uit afleidt.
  • Controleer elke tabeldimensie afzonderlijk.
  • Waarschuwingen en sanitizers helpen programmeerfouten op geteste paden te detecteren.

6.2 Lees en kopieer tekst binnen de grenzen van de buffer

Degene die invoer aanlevert, bepaalt de lengte ervan. Geef leesfuncties een werkelijke buffergrens en controleer wat ze teruggeven. Bepaal daarna of de gegevens een volledige regel vormen en of ze binnen de eigen regels van het programma passen.

Lees één woord met een veldbreedte

Voor char word[16]; leest scanf("%15s", word) hoogstens vijftien tekens die geen witruimte zijn en voegt '\0' toe. De veldbreedte laat één van de zestien posities vrij voor het afsluitende nulteken. %s zonder veldbreedte heeft geen maximale lengte; gets had hetzelfde fundamentele probleem voor regels en is in C11 uit C verwijderd.

Controleer of scanf 1 teruggeeft voordat je een woord gebruikt. De functie leest een woord, geen hele regel met spaties. Overtollige bytes van het woord kunnen ongelezen blijven, dus een veldbreedte beschermt de opslag zonder te bepalen of de hele invoer aanvaardbaar is.

Lees een regel met fgets
char buf[8];
if (fgets(buf, sizeof buf, stdin) == NULL) {
    return 1;
}

fgets leest hier hoogstens zeven tekens en schrijft een afsluitende nulbyte. De functie behoudt een nieuweregelteken als ze dat vóór de grens bereikt. Een regel met “hello” en een nieuweregelteken wordt "hello\n" plus het afsluitende nulteken. Een laatste regel die bij het bestandseinde zonder nieuweregelteken eindigt, kan nog steeds met succes worden gelezen. Als vóór het bestandseinde geen tekens zijn gelezen of als een leesfout optrad, is het resultaat NULL; verwerk die buffer dan niet als nieuwe invoer.

Onderscheid een opgeslagen nieuweregelteken van een onvolledige regel

Zoek een nieuweregelteken voordat je het verwijdert:

size_t end = strcspn(buf, "\n");
int had_newline = buf[end] == '\n';
buf[end] = '\0';

Neem <string.h> op. Als er geen nieuweregelteken is opgeslagen, is end de tekstlengte en overschrijft de toewijzing het bestaande afsluitende nulteken. Een ontbrekend nieuweregelteken kan duiden op een onvolledige regel, een buffer die precies vóór een nieuweregelteken is gevuld of een laatste regel die bij het bestandseinde eindigde. Op zichzelf bewijst dit niet dat er nog tekens over zijn.

Voor de invoerregel “abcdefghij” en een buffer van acht bytes slaat de eerste leesbewerking “abcdefg” op en kan de volgende “hij\n” lezen. Als je het tweede deel als een afzonderlijk antwoord behandelt, splits je één logische invoer. Een programma kan een regel zonder vereist nieuweregelteken afwijzen of de rest ervan lezen en weggooien voordat het opnieuw om invoer vraagt. Leg het invoerbeleid vast voordat je het implementeert.

Gebruik 32 bytes voor maximaal dertig ASCII-tekens in een naam plus een nieuweregelteken en een afsluitend nulteken. Voor UTF-8 is dit een grens in bytes: dertig weergegeven tekens kunnen meer opslagruimte nodig hebben.

Kopieer pas na controle van alle vereisten
char user[10];
if (argc != 2) return 1;
if (strlen(argv[1]) >= sizeof user) {
    puts("Name too long");
    return 1;
}
strcpy(user, argv[1]);

De controle van het aantal argumenten komt eerst. De lengtecontrole laat daarna hoogstens negen tekstbytes toe en laat de tiende positie vrij voor nul. Een schrijffunctie moet de capaciteit expliciet ontvangen, zoals in read_name(out, capacity), omdat sizeof out daarbinnen een pointerparameter meet.

Lees voor numerieke invoer een volledige regel en converteer die met strtol. Controleer daarbij de eindpositie, een eventuele bereikfout en het toegestane waardenbereik. Een mislukte scanf("%d", &n) kan de problematische tekst ongelezen laten staan; als je de aanroep blindelings herhaalt, kan die op dezelfde bytes eindeloos blijven mislukken. Controleer minimaal het aantal geslaagde toewijzingen en handel de ongeldige invoer af voordat je het opnieuw probeert.

Belangrijkste punten

  • Stel de veldbreedte voor het lezen van een woord in op één minder dan de buffercapaciteit.
  • Controleer een leesresultaat voordat je de buffer gebruikt.
  • fgets behoudt een nieuweregelteken alleen als de functie er binnen de grens één bereikt.
  • Een ontbrekend nieuweregelteken bewijst op zichzelf niet dat invoer overblijft.
  • Houd rekening met tekstbytes, een eventueel behouden nieuweregelteken en het afsluitende nulteken.
  • Controleer of een argument aanwezig is voordat je de tekst meet en kopieert.
  • Geef de buffercapaciteit door aan een functie die naar de buffer schrijft.

Arrays en strings in C: voorbeelden

6 Walkthroughs

1

Volg een array van declaratie tot functieaanroep

13 stappen
Volg geïnitialiseerde elementen, bereken een gemiddelde en deel arraygegevens met een functie.
  1. 1
    IntroductieGeef meerdere waarden één naam

    Een array bevat een vast aantal elementen van één type. Een lus selecteert posities, terwijl een functie via een arrayparameter op dezelfde opslag kan werken.

    Volg vier sensormetingen vanaf hun declaratie via een totaal naar een gemiddelde. Houd bij elke stap drie grootheden apart: het aantal elementen, de laatste geldige index en het aantal bytes dat voor opslag wordt gebruikt.

  2. 2
    VoorbeeldLees de beginposities
    int readings[4] = {11, 16, 13};
    

    Er zijn vier elementen, met indexen 0 tot en met 3. De initialisator geeft 11, 16 en 13 op de eerste drie posities. De weggelaten waarde wordt nul, dus de array is {11, 16, 13, 0}.

    De nul volgt uit een initialisatieregel en is geen teken dat de array maar drie elementen heeft. Bij een lokale array zonder initialisator moet je de waarden vóór gebruik schrijven.

  3. 3
    UitlegTel elementen bij de declaratie
    size_t n = sizeof readings / sizeof readings[0];
    

    De teller meet alle vier de elementen en de noemer meet er één. Waar int vier bytes inneemt, is dit zestien gedeeld door vier, met 4 als resultaat. Dezelfde expressie werkt bij een andere grootte van een geheel getal.

    readings[3] is het laatste element. readings[4] ligt voorbij het einde, ook al is het aantal elementen van de array 4. size_t is het type zonder teken dat voor dit grootteloopresultaat wordt gebruikt.

  4. 4
    UitdagingWijzig de laatste meting

    De declaratie is nog steeds int readings[4] = {11, 16, 13};. Welke toewijzing vervangt de nul door 20 en wat is het resulterende aantal?

    ---reveal---

    Gebruik readings[3] = 20;. De resulterende waarden zijn {11, 16, 13, 20} en het aantal blijft 4. Een toewijzing aan een element wijzigt een waarde, niet het aantal posities. Index 4 zou één positie te ver zijn en kan geen opslag aan deze array toevoegen.

  5. 5
    VoorbeeldBouw het totaal iteratie voor iteratie op
    int sum = 0;
    for (size_t i = 0; i < n; i++) {
        sum += readings[i];
    }
    

    Bij {11, 16, 13, 20} wordt de lopende som achtereenvolgens 11, 27, 40 en 60. Na index 3 wordt i gelijk aan 4 en mislukt de test vóór een volgende toegang.

    Door de som bij nul te laten beginnen, heeft de eerste waarde een schoon startpunt. De strikte bovengrens laat de lus precies de vier bestaande elementen bezoeken.

  6. 6
    KernpuntBehoud een gemiddelde met decimalen

    Voor deze vier metingen geeft (double) sum / n de waarde 15,0. De expliciete typeconversie vindt vóór de deling plaats, zodat de bewerking een resultaat met decimalen kan opleveren wanneer het totaal verandert.

    Als het totaal 61 was, zou geheeltallige deling sum / 4 de waarde 15 opleveren, terwijl sum / 4.0 15,25 zou opleveren. Als je het al afgeknotte geheeltallige resultaat aan een double toewijst, krijg je alleen 15,0; het weggelaten deel achter de komma komt niet terug.

  7. 7
    TipKies een geldige beginkandidaat

    Een zoekopdracht naar het maximum heeft een beginwaarde uit een niet-lege array nodig. Initialiseer voor {-6, -2, -9} max met -6 en vervang die waarde door -2 wanneer die grotere waarde verschijnt. De laatste vergelijking laat -2 ongewijzigd.

    Als je max met nul initialiseert, krijg je nul terug, terwijl die waarde niet voorkomt. Bij lineair zoeken is de bijbehorende stopregel ook belangrijk: één afwijkende waarde bewijst niet dat de zoekopdracht is mislukt; latere elementen moeten nog worden gecontroleerd.

  8. 8
    UitdagingBlijf zoeken

    Een zoekopdracht naar 13 controleert {11, 16, 13, 20}. Na controle van index 0 geeft de zoekopdracht -1 terug omdat 11 verschilt van 13. Wat moet veranderen en waar staat de overeenkomst?

    ---reveal---

    Blijf na een verschil controleren en geef pas -1 terug als geen enkele kandidaat voldoet. Hier verschilt ook index 1, maar index 2 komt overeen. Een geslaagde zoekopdracht kan daar stoppen. De mislukte eerste vergelijking beschrijft één element, niet de volledige array.

  9. 9
    VerbandBreng de gemiddeldeberekening onder in een functie
    double mean(const int a[], size_t n);
    

    De arrayparameter deelt de toegang tot de elementen van de aanroepende functie. De parameter bevat hun aantal niet, dus geeft de tweede parameter dat door. const voorkomt onbedoeld schrijven via a in deze bewerking die alleen leest.

    Bereken het aantal waar readings is gedeclareerd en roep mean(readings, n) aan. Binnen mean meet sizeof a een pointerparameter in plaats van de array met vier elementen van de aanroepende functie.

  10. 10
    VoorbeeldHandel het lege bereik af vóór de deling
    double mean(const int a[], size_t n) {
        if (n == 0) return 0.0;
        int sum = 0;
        for (size_t i = 0; i < n; i++) sum += a[i];
        return (double) sum / n;
    }
    

    Voor de vier kleine metingen blijft het totaal 60 en is het resultaat 15,0. Als n gelijk is aan nul, is het gekozen resultaat 0,0 en vindt geen arraytoegang of deling plaats. De aanroepende functie moet een correct bruikbaar aantal doorgeven.

  11. 11
    UitdagingLeg een gewijzigde array van de aanroepende functie uit

    Een andere functie ontvangt int a[], size_t n en zet elke bezochte a[i] op nul. Wat gebeurt er met de metingen van de aanroepende functie en hoe kan een functie die alleen een totaal berekent zulke schrijfbewerkingen voorkomen?

    ---reveal---

    De elementen van de aanroepende functie worden nul omdat de functie naar de gedeelde arrayopslag schrijft. De vier posities bestaan nog steeds. Een functie die alleen een totaal berekent, moet const int a[] gebruiken, waardoor toewijzingen aan elementen via a ongeldig worden. Een scalaire kopie van n wijzigen zou de tellervariabele van de aanroepende functie niet veranderen.

  12. 12
    UitdagingGebruik alleen de metingen die bestaan

    Een buffer heeft capaciteit 10, maar slechts vier metingen zijn in gebruik. Voordat je een gemiddelde berekent, moet een nieuwe meting worden toegevoegd. Welke controles en welk aantal moet het programma gebruiken?

    ---reveal---

    Controleer of het aantal gebruikte elementen, 4, kleiner is dan capaciteit 10. Schrijf op index 4 en werk daarna het aantal bij naar 5. Geef dat aantal gebruikte elementen door aan mean, zodat de functie precies de vijf waarden verwerkt. Als je de capaciteit zou doorgeven, zouden posities worden meegenomen waarvoor geen metingen zijn opgegeven.

  13. 13
    SamenvattingVerbind elk aantal met zijn taak

    Een declaratie maakt de posities, een lus bezoekt de gebruikte posities en een functie ontvangt het bruikbare aantal expliciet. Een parameter die alleen wordt gelezen, voorkomt dat berekeningen de invoer overschrijven.

    Houd bij het beoordelen van arraycode de eerste en laatste bezochte index en de toestand van elke accumulator bij. Begin bij achterwaartse size_t-lussen op n, ga door zolang i positief is en benader a[i - 1], zodat je bij een leeg bereik niets van nul hoeft af te trekken.

2

Voer tekstbewerkingen uit op opgeslagen bytes

16 stappen
Bouw een string met afsluitend nulteken op, transformeer die en volg daarna een UTF-8-teken van meerdere bytes.

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

  1. 1
    IntroductieLet op zowel tekst als opslag
  2. 2
    UitlegLees een teken als code
  3. 3
    UitdagingLoop terug binnen het alfabet
  4. 4
    VoorbeeldMaak een beschrijfbare string
  5. 5
    KernpuntVerplaats het einde zonder de opslag te verplaatsen
  6. 6
    UitdagingLaat een positie voor het einde vrij
  7. 7
    VoorbeeldHoud lezen en schrijven gescheiden
  8. 8
    VoorbeeldMaak de nieuwe string af
  9. 9
    UitdagingKeer letters om zonder het afsluitende nulteken
  10. 10
    TipClassificeer bytes met geldige argumenten
  11. 11
    VoorbeeldBreng een UTF-8-letter met accent in beeld
  12. 12
    UitlegHerken vervolgbytes
  13. 13
    UitdagingMeet een nieuw gecodeerd woord
  14. 14
    VerbandKies bewerkingen die tekens heel houden
  15. 15
    UitdagingControleer een voorgestelde kortere aanpak voor tekst
  16. 16
    SamenvattingZoek het einde en de tekengrenzen
3

Stel tekst samen, doorzoek die en maak die veilig op

14 stappen
Controleer capaciteiten, interpreteer zoekresultaten en handel afkapping en overlap af.

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

  1. 1
    IntroductieBepaal het contract van elke functie
  2. 2
    VoorbeeldBegin met een volledige kopie
  3. 3
    VoorbeeldVoeg toe bij het bestaande einde
  4. 4
    UitdagingBepaal of nog iets toevoegen past
  5. 5
    UitlegGebruik nul om gelijke tekst te herkennen
  6. 6
    VoorbeeldLees vanaf een gevonden locatie
  7. 7
    UitdagingOnderzoek een opdrachtcontrole op basis van een beginstuk
  8. 8
    TipVerwacht dat tokenisatie tekst verandert
  9. 9
    VoorbeeldMaak tekst op binnen een bekende capaciteit
  10. 10
    UitdagingInterpreteer dezelfde opmaak met minder ruimte
  11. 11
    KernpuntEen grens kan bronbytes tellen
  12. 12
    VoorbeeldVerplaats een overlappend gebied
  13. 13
    UitdagingBehoud een volledig resultaat na het verschuiven
  14. 14
    SamenvattingOnderscheid bytes van tekstresultaten
4

Lees een tabel en kies een opslagvorm voor een lijst met strings

10 stappen
Volg rij- en kolomtotalen, geef een tabel door en reserveer ruimte per string.

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

  1. 1
    IntroductieGeef elke positie twee indexen
  2. 2
    VoorbeeldPlaats waarden in rijen
  3. 3
    VoorbeeldStel een rijtotaal opnieuw in aan het begin van de rij
  4. 4
    UitdagingVerander de groepering en behoud de indexen
  5. 5
    UitlegGeef een functie de rijbreedte door
  6. 6
    VoorbeeldReserveer per rij één string met afsluitend nulteken
  7. 7
    KernpuntVerwijzingen staan verschillende stringlengtes toe
  8. 8
    UitdagingVervang tekst in de juiste opslag
  9. 9
    UitdagingHoud beide dimensies geldig in een functie
  10. 10
    SamenvattingHoud de vorm van de tabel zichtbaar
5

Valideer een selectie via de opdrachtregel

11 stappen
Interpreteer gegroepeerde argumenten en converteer een gecontroleerd getal naar een bruikbare selectie.

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

  1. 1
    IntroductieOntvang tekst voordat je die interpreteert
  2. 2
    VoorbeeldKoppel een shellopdracht aan elementen
  3. 3
    UitlegControleer het aantal vóór de conversie
  4. 4
    UitdagingGroepeer één naam correct
  5. 5
    TipEen teruggegeven geheel getal is niet genoeg
  6. 6
    VoorbeeldOnderzoek een volledige conversie
  7. 7
    UitlegControleer het eigen bereik van de selectie
  8. 8
    UitdagingHerken tekst die gedeeltelijk numeriek lijkt
  9. 9
    KernpuntSchrijfwijze en waarde zijn afzonderlijke regels
  10. 10
    UitdagingCombineer groepering en numerieke validatie
  11. 11
    SamenvattingControleer aanwezigheid, conversie en doel
6

Herstel een begrensde invoerroutine

13 stappen
Volg regeldelen, houd ruimte voor een afsluitend nulteken en kopieer alleen een volledig geaccepteerd resultaat.

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

  1. 1
    IntroductieBegrens de schrijfbewerking en controleer het resultaat
  2. 2
    VoorbeeldZoek de extra schrijfbewerking
  3. 3
    KernpuntEen geldige laatste positie kan het einde bevatten
  4. 4
    UitdagingControleer een index die uit een byte is afgeleid
  5. 5
    UitlegLees een woord met ruimte voor het afsluitende nulteken
  6. 6
    VoorbeeldLees één begrensd deel van een regel
  7. 7
    VoorbeeldBekijk waar een lange regel wordt gesplitst
  8. 8
    TipOnderzoek een nieuweregelteken voordat je het verwijdert
  9. 9
    UitdagingReserveer ruimte voor een volledige regel
  10. 10
    VoorbeeldControleer een kopie vóór het schrijven
  11. 11
    UitdagingOnderscheid een veilig beginstuk van een geaccepteerd antwoord
  12. 12
    VerbandGebruik diagnostische meldingen en redeneer over grenzen
  13. 13
    SamenvattingHoud vier controles apart

Arrays en strings in C: oefenvragen

60 Oefenvragen in 6 sets

Set 1: Arraywaarden, lussen en functieaanroepen10 Oefenvragen

Vraag 1 van 10

Welke waarde staat op a[3] na int a[5] = {6, 9};?
  1. 6
  2. 9
  3. 0
  4. Een onbepaalde waarde
Toon antwoordVerberg antwoord
Antwoord: C
De initialisator geeft de eerste twee waarden en C initialiseert de overige elementen op nul. Index 3 bevat daarom 0. Onbepaalde waarden zouden een probleem zijn bij een lokale array die zonder initialisator is gedeclareerd; dat verschilt van deze gedeeltelijk geïnitialiseerde array.

Vraag 2 van 10

Een array is gedeclareerd als int a[7];, waarbij int 4 bytes inneemt. Welk paar geeft sizeof a en de laatste geldige index?
  1. 28 bytes; index 7
  2. 28 bytes; index 6
  3. 7 bytes; index 6
  4. 24 bytes; index 6
Toon antwoordVerberg antwoord
Antwoord: B
Zeven elementen van vier bytes nemen 28 bytes in en indexering vanaf nul eindigt bij 6. De laatste index is één minder dan het aantal. Index 7 gebruiken verwart het aantal elementen met de positie van het laatste element en zou buiten deze array toegang zoeken.

Vraag 3 van 10

Welke waarde moet een zoekopdracht naar het maximum voor int a[] = {-12, -4, -9}; teruggeven?
  1. -4
  2. 0
  3. -12
  4. -9
Toon antwoordVerberg antwoord
Antwoord: A
De grootste opgeslagen waarde is -4 omdat die groter is dan zowel -12 als -9. Een zoekopdracht naar het maximum moet met een echt element beginnen en dat bijwerken wanneer een grotere waarde verschijnt. Beginnen bij nul zou een waarde teruggeven die deze array met uitsluitend negatieve waarden niet bevat.

Vraag 4 van 10

Wat drukt deze code af?

int a[] = {3, 8, 5, 2};
int sum = 0;
for (int i = 1; i < 4; i++) sum += a[i];
printf("%d", sum);
  1. 18
  2. 13
  3. 16
  4. 15
Toon antwoordVerberg antwoord
Antwoord: D
De lus begint bij index 1 en telt dus 8, 5 en 2 op tot 15. Index 0 wordt nooit bezocht. Het totaal 18 omvat de eerste 3 en zou alleen juist zijn voor een lus die bij nul begint, wat deze code niet doet.

Vraag 5 van 10

Een zoekopdracht naar 7 in {2, 9, 7, 4} geeft bij het eerste ongelijke element -1 terug. Waar moet de retour bij mislukking plaatsvinden?
  1. Binnen de lus na elke ongelijke kandidaat.
  2. Na de lus die elke kandidaat heeft gecontroleerd.
  3. Vóór de controle van het laatste kandidaatelement.
  4. Direct na de eerste verhoging in de lus.
Toon antwoordVerberg antwoord
Antwoord: B
De eerste waarde, 2, komt niet overeen, maar index 2 bevat wel 7. Mislukking is pas gerechtvaardigd wanneer de lus zonder overeenkomst eindigt. Terugkeren na één verschil behandelt informatie over één element als informatie over de hele array.

Vraag 6 van 10

Welke array ontstaat nadat je a[i] met a[5 - 1 - i] wisselt voor i = 0 en i = 1, uitgaande van {4, 7, 1, 8, 6}?
  1. {6, 7, 1, 8, 4}
  2. {4, 7, 1, 8, 6}
  3. {6, 8, 1, 7, 4}
  4. {8, 6, 1, 4, 7}
Toon antwoordVerberg antwoord
Antwoord: C
De eerste wissel verwisselt 4 en 6; de tweede verwisselt 7 en 8. De middelste waarde 1 blijft staan, waardoor de omgekeerde array ontstaat. Het gedeeltelijk omgekeerde resultaat met 7 en 8 op hun oorspronkelijke plaats houdt alleen rekening met de eerste wissel, terwijl twee wissels zijn opgegeven.

Vraag 7 van 10

Een functie void set_first(int a[]) voert a[0] = 11; uit. Wat is het eerste element van de array van de aanroepende functie na de aanroep?
  1. Het bevat 11.
  2. Het is ongewijzigd.
  3. Het is ongedefinieerd.
  4. Het bevat 0.
Toon antwoordVerberg antwoord
Antwoord: A
De parameter geeft de functie toegang tot de elementen van de aanroepende functie, dus deze toewijzing verandert het oorspronkelijke eerste element in 11. Er is geen afzonderlijke kopie van het element die wordt weggegooid wanneer de functie terugkeert. Een array doorgeven verschilt hier van een scalaire waarde afzonderlijk doorgeven.

Vraag 8 van 10

Binnen void f(int a[], size_t n) nemen pointers 8 bytes en gehele getallen 4 bytes in. Wat is sizeof a / sizeof a[0]?
  1. n, bij elke aanroep
  2. 8, ongeacht n
  3. 4, bij elke aanroep
  4. 2, ongeacht n
Toon antwoordVerberg antwoord
Antwoord: D
De arrayparameter wordt omgezet naar een pointer, dus de grootte ervan is in deze omgeving 8 bytes. Eén element is 4 bytes, wat 2 geeft. Dat resultaat achterhaalt de arraylengte van de aanroepende functie niet; de functie moet voor het aantal elementen de afzonderlijk doorgegeven n gebruiken.

Vraag 9 van 10

Een functie die alleen leest, gebruikt size_t voor n en i. Ze moet een array achterwaarts afdrukken en werken voor n = 0. Welke parameter en lus voldoen aan beide vereisten?
  1. const int a[]; begin i bij n - 1, test i >= 0, benader a[i].
  2. int a[]; begin i bij n, test i > 0, benader a[i].
  3. const int a[]; begin i bij n, test i > 0, benader a[i - 1].
  4. int a[]; begin i bij n - 1, test i > 0, benader a[i - 1].
Toon antwoordVerberg antwoord
Antwoord: C
De const-parameter voorkomt schrijfbewerkingen via a, terwijl beginnen bij n aftrekking zonder teken voorkomt wanneer het bereik leeg is. Door vervolgens i - 1 te benaderen, bezoek je de elementen vanaf het laatste geldige element tot en met nul. Beginnen bij n - 1 met een i zonder teken en testen op i >= 0 veroorzaakt modulaire terugloop en levert nooit een stopvoorwaarde op.

Vraag 10 van 10

Wat garandeert C nadat een programma a[4] uit int a[4] leest?
  1. Geen resultaat is gegarandeerd.
  2. De leesbewerking geeft nul terug.
  3. De leesbewerking geeft a[3] terug.
  4. De leesbewerking loopt altijd vast.
Toon antwoordVerberg antwoord
Antwoord: A
De geldige indexen van de array zijn 0 tot en met 3, dus deze leesbewerking ligt buiten de grenzen en heeft ongedefinieerd gedrag. C stelt geen verplicht resultaat voor die bewerking. Vastlopen is mogelijk, maar als je dat elke keer verwacht, behandel je één mogelijk verschijnsel ten onrechte als een garantie van de taal.
Set 2: Tekens, stringeinden en UTF-810 Oefenvragen

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

Vraag 1 van 10

Welke numerieke waarde levert '6' - '0' op?
  1. 54
  2. 48
  3. 0
  4. 6
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Welk teken ontstaat in ASCII uit ('W' - 'A' + 5) % 26 + 'A'?
  1. 'B'
  2. 'A'
  3. 'C'
  4. '\\'
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Hoe moet een char-byte c aan isalpha worden doorgegeven zonder risico op een negatief byteargument?
  1. isalpha((signed char) c)
  2. isalpha(c - '0')
  3. isalpha((unsigned char) c)
  4. isalpha((int) c)
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Wat zijn de tekstlengte en arraygrootte van char color[] = "amber";?
  1. Lengte 6; grootte 6
  2. Lengte 5; grootte 6
  3. Lengte 5; grootte 5
  4. Lengte 6; grootte 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Welke string blijft over na char s[8] = "planet"; s[3] = '\0'; s[0] = 'P';?
  1. "Pla"
  2. "Planet"
  3. "Pla0et"
  4. "Plan"
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Waarom is char tag[4] = "east"; printf("%s", tag); onveilig?
  1. De initialisator heeft te weinig tekens.
  2. Het formaat drukt maar één byte af.
  3. De array heeft geen afsluitende nulbyte.
  4. De tekens van de array zijn alleen-lezen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een filter behoudt alleen letters uit "m8n2p" en schrijft die naar een uitvoerarray die groot genoeg is. Wat moet het na de laatste p schrijven?
  1. Een nulbyte op uitvoerindex 2
  2. Het cijfer nul op uitvoerindex 3
  3. Een nulbyte op uitvoerindex 5
  4. Een nulbyte op uitvoerindex 3
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een programma keert char s[] = "lamp"; om door s[i] te wisselen met s[len - i] zolang i < len / 2, waarbij len gelijk is aan strlen(s). Wat zijn de uiteindelijke strlen(s) en de ongewijzigde capaciteit?
  1. Lengte 4; capaciteit 5
  2. Lengte 0; capaciteit 5
  3. Lengte 0; capaciteit 4
  4. Lengte 5; capaciteit 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

In UTF-8 gebruikt de vooraf samengestelde “é” twee bytes. Wat zijn strlen en de minimale opslagruimte voor de string “écho”?
  1. 5 tekstbytes; 6 opslagbytes
  2. 4 tekstbytes; 5 opslagbytes
  3. 5 tekstbytes; 5 opslagbytes
  4. 6 tekstbytes; 7 opslagbytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een geldige UTF-8-string heeft de bytes 41 C3 B1 42 00. Een programma schrijft nul op byte-index 2. Wat zijn de nieuwe strlen en de UTF-8-geldigheid van de tekst?
  1. Lengte 2; geldige UTF-8
  2. Lengte 2; ongeldige UTF-8
  3. Lengte 3; geldige UTF-8
  4. Lengte 1; ongeldige UTF-8
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Kies en controleer bibliotheekbewerkingen10 Oefenvragen

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

Vraag 1 van 10

Wat is de kleinste char-arraycapaciteit voor een volledige kopie van "silver"?
  1. 7 bytes
  2. 6 bytes
  3. 8 bytes
  4. 5 bytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Kan char label[10] = "blue"; strcat(label, "berry"); het volledige resultaat opslaan?
  1. Nee; het resultaat heeft 11 bytes nodig.
  2. Ja; het resultaat heeft 9 bytes nodig.
  3. Ja; het resultaat heeft 10 bytes nodig.
  4. Nee; het resultaat heeft 12 bytes nodig.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Binnen een functie wordt een automatische lokale array gebruikt als char out[20]; strcat(out, "go");. Waarom is dit een ongeldige manier om een string op te bouwen?
  1. out is te klein voor de toegevoegde tekst.
  2. out heeft geen vastgelegd afsluitend nulteken.
  3. strcat vereist een pointerarray voor out.
  4. strcat kan niet uit een stringliteral kopiëren.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Welke voorwaarde herkent gelijke tekst in twee geldige strings a en b?
  1. a == b
  2. strcmp(a, b) > 0
  3. strlen(a) == strlen(b)
  4. strcmp(a, b) == 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Welke tekst begint bij p na een geslaagde char *p = strrchr("archive.backup.bin", '.');?
  1. ".bin"
  2. "bin"
  3. ".backup.bin"
  4. "archive.backup"
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een beschrijfbare array bevat "on,,off;wait". Aanroepen gebruiken strtok(s, ",;") en herhalen daarna strtok(NULL, ",;") tot NULL. Welke tokens ontstaan en blijft s behouden?
  1. “on”, “”, “off”, “wait”; s wordt gewijzigd.
  2. “on”, “off;wait”; s wordt gewijzigd.
  3. “on”, “off”, “wait”; s wordt gewijzigd.
  4. “on”, “off”, “wait”; s blijft behouden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Wat slaat char b[6]; int r = snprintf(b, sizeof b, "%s:%d", "job", 84); op en wat geeft de aanroep terug?
  1. Slaat “job:8” op en geeft 5 terug.
  2. Slaat “job:84” op en geeft 6 terug.
  3. Slaat “job:” op en geeft 4 terug.
  4. Slaat “job:8” op en geeft 6 terug.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een array van zeven bytes begint met "AB". Het programma voert strncat(b, "123456", sizeof b - strlen(b) - 1) uit. Wat is de resulterende tekst en hoeveel opgeslagen bytes zijn er inclusief nul?
  1. “AB12345”; 8 bytes
  2. “AB1234”; 7 bytes
  3. “AB123”; 6 bytes
  4. “AB123456”; 9 bytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Wat bevat s als string na char s[] = "wxyz"; memmove(s, &s[1], 4);?
  1. "xyzz"
  2. "wxy"
  3. "xyz"
  4. "yz"
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Twee double-arrays zijn element voor element gelijk, maar de ene slaat positieve nul op waar de andere negatieve nul opslaat. Wat kan een bytevergelijking vaststellen?
  1. Of hun opgeslagen representaties overeenkomen.
  2. Of hun numerieke waarden verschillen.
  3. Of de ene meer elementen heeft.
  4. Of beide waarden positieve nul zijn.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Tabellen, tekstlijsten en invoer bij het starten10 Oefenvragen

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

Vraag 1 van 10

Wat is het laatste geldige rij- en kolompaar voor int table[4][6]?
  1. Rij 4, kolom 6
  2. Rij 3, kolom 5
  3. Rij 3, kolom 6
  4. Rij 4, kolom 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Waar int 4 bytes inneemt, wat is sizeof table[0] voor int table[3][5]?
  1. 12 bytes
  2. 15 bytes
  3. 60 bytes
  4. 20 bytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Een tabel is {{2, 7, 4}, {6, 1, 5}}. Een lus initialiseert sum één keer op nul vóór beide lussen, telt telkens één rij op en drukt na elke rij af zonder sum opnieuw in te stellen. Welke waarden worden afgedrukt en hoe wordt dit gecorrigeerd?
  1. 13 en dan 25; stel sum vóór elke rij opnieuw in.
  2. 13 en dan 12; stel sum vóór elke rij opnieuw in.
  3. 13 en dan 25; stel sum vóór elke cel opnieuw in.
  4. 8 en dan 16; stel sum vóór elke kolom opnieuw in.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Welke arrayparameter met vaste breedte ontvangt rijen met elk zeven gehele getallen?
  1. int g[7][]
  2. int g[][]
  3. int g[][7]
  4. int g[][6]
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Wat is de kleinste rijbreedte voor char names[3][width] om “Iris”, “Magnolia” en “Ash” als volledige ASCII-strings op te slaan?
  1. 8 bytes
  2. 10 bytes
  3. 16 bytes
  4. 9 bytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een programma heeft char rows[2][5] = {"oak", "elm"}; en const char *refs[] = {"oak", "elm"};. Het moet het eerste woord in de lijst vervangen door de volledige tekst “cedar”. Welke aanpak is geldig voor elke opslagvorm?
  1. Kopieer “cedar” naar rows[0]; wijs refs[0] opnieuw toe.
  2. Wijs de rijkopie af; laat refs[0] naar “cedar” verwijzen.
  3. Wijs de rijkopie af; kopieer “cedar” naar refs[0].
  4. Wijs rows[0] = "cedar" toe; wijs refs[0] opnieuw toe.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

In een gewone shell wordt een programma uitgevoerd als ./mark "West Room" 9. Wat zijn argc en argv[2]?
  1. argc is 3; argv[2] is “9”.
  2. argc is 4; argv[2] is “Room”.
  3. argc is 2; argv[2] is NULL.
  4. argc is 3; argv[2] is “West Room”.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een programma vereist twee gebruikersargumenten. Het controleert argc == 2 en roept daarna strlen(argv[2]) aan. Welke controle van het aantal en welke laatste index passen bij de werkelijke interface?
  1. Controleer argc == 2; lees gebruikersargumenten tot en met index 2.
  2. Controleer argc == 3; lees gebruikersargumenten tot en met index 3.
  3. Controleer argc == 3; lees gebruikersargumenten tot en met index 2.
  4. Controleer argc == 2; lees gebruikersargumenten tot en met index 1.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Wat geven het resultaat en de eindpointer aan nadat strtol “42rest” decimaal heeft geconverteerd?
  1. Waarde 0; er zijn geen tekens verwerkt.
  2. Waarde 42; onverwerkte tekst begint met r.
  3. Waarde 42; de hele invoer is verwerkt.
  4. Waarde 42; er moet een bereikfout zijn opgetreden.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een sleutel met alleen cijfers past in long maar niet in int. Het programma converteert de sleutel expliciet naar int voordat het key % 26 berekent. Hoe kan het de bedoelde verschuiving behouden zonder eerst naar een smaller type te converteren?
  1. Converteer eerst naar int en bereken daarna de rest.
  2. Gebruik atoi en bereken daarna de rest als int.
  3. Bereken de rest als long en converteer daarna het resultaat 0–25.
  4. Controleer opnieuw de cijfers en converteer de oorspronkelijke waarde.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 5: Invoergrenzen en volledige resultaten10 Oefenvragen

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

Vraag 1 van 10

Welke grootste veldbreedte voor %s laat in char word[18] een positie vrij voor een afsluitend nulteken?
  1. %18s
  2. %16s
  3. %17s
  4. %19s
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een geslaagde fgets leest uit de invoer "pine\n" naar een array van 10 bytes. Wat zijn strlen van het resultaat en de index van het afsluitende nulteken?
  1. Lengte 4; index nulteken 4
  2. Lengte 5; index nulteken 6
  3. Lengte 6; index nulteken 5
  4. Lengte 5; index nulteken 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

De invoerregel is “qrstuvwxy” gevolgd door een nieuweregelteken. Twee fgets-aanroepen gebruiken dezelfde char b[6] zonder andere leesbewerkingen. Welke tekst slaat de tweede aanroep op?
  1. "uvwxy"
  2. "vwxy\n"
  3. "vwxy"
  4. "qrst\n"
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een programma staat 12 ASCII-teksttekens toe in een naam die als volledige regel met afsluitend nieuweregelteken wordt ingevoerd. Het moet het nieuweregelteken onderzoeken en daarna alleen de naam in een afzonderlijke array opslaan. Welke minimale invoer- en eindcapaciteit zijn nodig?
  1. Invoer 14 bytes; eindarray 13 bytes
  2. Invoer 13 bytes; eindarray 13 bytes
  3. Invoer 14 bytes; eindarray 12 bytes
  4. Invoer 13 bytes; eindarray 12 bytes
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een buffer van 9 bytes ontvangt een geslaagd fgets-resultaat met lengte 8 zonder nieuweregelteken. Er is geen verdere invoer onderzocht. Wat kun je over de logische regel concluderen?
  1. De regel is zeker langer dan acht tekens.
  2. De regel kan daar eindigen of er kan meer invoer wachten.
  3. De regel eindigde zeker al bij het bestandseinde.
  4. Het nieuweregelteken is door fgets gelezen en verwijderd.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Waarom is counts[c - 'a']++ onveilig voor ongecontroleerde tekeninvoer?
  1. De afgeleide index kan buiten de array liggen.
  2. De verhoging kan geen gehele tellers bijwerken.
  3. De aftrekking geeft altijd een negatieve waarde.
  4. De expressie vergelijkt twee volledige strings.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een char-array van zes bytes wordt op indexen 0 tot en met 5 met letters gevuld en daarna aan strlen doorgegeven. Welke eigenschap ontbreekt?
  1. Een ongebruikte byte buiten de array voor nul.
  2. Een expliciet nieuweregelteken na de laatste letter.
  3. Een afsluitend nulteken binnen de zes geldige posities.
  4. Een afzonderlijk geheel getal met de capaciteit in bytes.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een programma slaat een volledige code op in char out[6]. Op de aanroep snprintf(out, sizeof out, "%s%d", "R-", 1234) volgt if (r > sizeof out) reject();, waarbij r de niet-negatieve retourwaarde is. Wat wordt opgeslagen, wijst die test het af en wat moet veranderen?
  1. “R-1234”; geaccepteerd; behoud de bestaande test.
  2. “R-123”; afgewezen; behoud de bestaande test.
  3. “R-12”; geaccepteerd; wijs af bij r >= capaciteit.
  4. “R-123”; geaccepteerd; wijs af bij r >= capaciteit.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een decimale parser wijst ERANGE af en vereist *end == '\0', maar controleert niet op end == start. Welke ongeldige invoer kan toch als nul worden geaccepteerd?
  1. Een lege string
  2. Een woord met letters
  3. Een cijfer gevolgd door een letter
  4. Een getal buiten het bereik van long
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een geldige UTF-8-string heeft de bytes 41 C3 A9 E2 82 AC 00. Een programma kopieert die naar char b[5] en reserveert de laatste positie voor nul. Wat zijn de resulterende strlen, het aantal beginbytes en de geldigheid van de codering?
  1. Lengte 4; beginbytes 3; geldige UTF-8.
  2. Lengte 4; beginbytes 3; ongeldige UTF-8.
  3. Lengte 3; beginbytes 3; geldige UTF-8.
  4. Lengte 4; beginbytes 2; ongeldige UTF-8.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 6: Volg en herstel gecombineerde C-bewerkingen10 Oefenvragen

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

Vraag 1 van 10

Welke array wordt gemaakt door int a[4] = {[2] = 9};?
  1. {0, 9, 0, 0}
  2. {0, 0, 9, 0}
  3. {9, 0, 0, 0}
  4. {0, 0, 0, 9}
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een array met acht posities heeft aantal 5. Eén waarde wordt toegevoegd op a[count] en count wordt verhoogd. Naar welke index is geschreven en wat is het nieuwe aantal?
  1. Index 5; aantal 6
  2. Index 4; aantal 6
  3. Index 6; aantal 6
  4. Index 5; aantal 5
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Wat is voor int g[3][4] de elementpositie van g[2][1] bij indexering vanaf nul in rijgewijze opslag?
  1. 7
  2. 8
  3. 10
  4. 9
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een shell voert ./pick "" blue uit. Hoe verschillen argv[1] en argv[argc]?
  1. argv[1] is een nulpointer; argv[argc] is een lege string.
  2. Beide elementen zijn lege strings met een eigen afsluitend nulteken.
  3. argv[1] is een lege string; argv[argc] is een nulpointer.
  4. Beide elementen zijn nulpointers die ontbrekende tekst aangeven.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een functie ontvangt int a[], size_t n en voert a[0] = 0; n = 0; uit. De aanroepende functie gaf {5, 8, 2} en aantal 3 door en berekent daarna de geheeltallige som van de elementen gedeeld door het aantal. Welk gemiddelde en welk aantal bij de aanroepende functie ontstaan?
  1. Gemiddelde 0; aantal aanroepende functie 3
  2. Gemiddelde 3; aantal aanroepende functie 3
  3. Gemiddelde 5; aantal aanroepende functie 3
  4. Ongedefinieerde deling; aantal aanroepende functie 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Een parser splitst beschrijfbare tekst “run,runner” bij komma’s met strtok. De parser verhoogt een teller wanneer strncmp(token, "run", 3) == 0. Welk aantal ontstaat, welk aantal moet exacte opdrachtherkenning geven en welke vergelijking herstelt dit?
  1. Huidig 2; exact 1; strcmp(token, "run") == 0.
  2. Huidig 1; exact 1; strcmp(token, "run") == 0.
  3. Huidig 2; exact 2; strncmp(token, "run", 2) == 0.
  4. Huidig 1; exact 2; strstr(token, "run") != NULL.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een array b van vijf bytes wordt gevuld met strncpy(b, "planet", sizeof b);. Het programma vereist het volledige oorspronkelijke woord. Welke oplossing voldoet aan die eis?
  1. Schrijf na het kopiëren nul in b[4].
  2. Voeg na het kopiëren nul toe met b[5].
  3. Geef sizeof b - 1 door en gebruik b dan ongewijzigd.
  4. Wijs het vóór het kopiëren af als te lang.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een programma gebruikt int values[4] = {14, 28, 35, 49};. Het converteert “04” volledig met decimale strtol, accepteert selecties 1–4 en leest daarna values[selected]. Het moet alle vier de selecties toestaan en selectie 1 aan de eerste waarde koppelen. Wat gebeurt er en welke indexregel herstelt dit?
  1. Het drukt 49 af; behoud index selected.
  2. Het drukt 49 af; gebruik index selected - 1.
  3. Het heeft ongedefinieerd gedrag; gebruik index selected - 1.
  4. Het heeft ongedefinieerd gedrag; vereis selectie < 4.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een functie read_text(char out[], size_t cap) roept fgets(out, sizeof out, stdin) aan. De aanroepende functie geeft een array van 30 bytes door; de pointergrootte is 8. Als “sunflower\n” klaarstaat, welk deel wordt opgeslagen, wat blijft over en welke grens moet sizeof out vervangen?
  1. “sunflower\n”; niets blijft over; gebruik cap.
  2. “sunflowe”; “r\n” blijft over; gebruik cap.
  3. “sunflow”; “er\n” blijft over; gebruik strlen(out).
  4. “sunflow”; “er\n” blijft over; gebruik cap.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een ASCII-letterreeksteller begint buiten een woord en verhoogt de teller telkens wanneer een letter op een niet-letter volgt. Hoeveel woorden telt die in “go2fast now!”?
  1. 3
  2. 2
  3. 4
  4. 1
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

85 Kernbegrippen, elk met een definitie

A

Aaneenschakeling
Aaneenschakeling voegt tekstreeksen op volgorde samen en vereist genoeg doelruimte voor de gecombineerde tekst en het afsluitende teken.
“north” en “east” samenvoegen vormt “northeast”, waarvoor als ASCII-string met afsluitend nulteken tien bytes nodig zijn.
Aantal gebruikte elementen
Het aantal gebruikte elementen legt vast hoeveel arrayelementen op dit moment tot de actieve gegevens behoren en bepaalt de grens van de verwerkingslus.
Als in een array van 50 elementen slechts 12 elementen zijn gevuld, stopt een gegevensverwerkingslus bij aantal 12.
Aantal geslaagde toewijzingen
Het aantal geslaagde toewijzingen is het aantal invoertoewijzingen dat scanf met succes heeft voltooid en wordt gebruikt om te controleren of gevraagde gegevens daadwerkelijk zijn gelezen.
Een leesbewerking voor één getal verwacht een retourwaarde van 1; bij niet-passende invoer kunnen dezelfde ongeldige bytes in de invoer blijven wachten.
Accumulator
Een accumulator is een variabele die een tussentijds resultaat verzamelt, zoals een som of aantal, terwijl een lus opeenvolgende waarden verwerkt.
Door sum bij nul te laten beginnen, kan elke sum += a[i] het volgende arrayelement aan het huidige totaal toevoegen.
Afkapping
Afkapping is het verlies van tekst voorbij een beschikbare lengtegrens. Dit kan overloop voorkomen maar toch de betekenis van de gegevens veranderen.
Een verkort bestandslabel kan naar andere tekst verwijzen, dus moet een interface die een volledige naam vereist een afgekapt resultaat afwijzen.
Afsluitend nulteken
Een afsluitend nulteken is het teken met waarde nul dat het einde van een C-string markeert en opslagruimte inneemt zonder bij te dragen aan de tekstlengte.
Door text[2] = '\0' te schrijven, stoppen stringfuncties na de eerste twee tekens in die array.
Afsluitstatus
Een afsluitstatus meldt aan de aanroepende omgeving dat een programma is voltooid; nul teruggeven vanuit main geeft een succesvolle voltooiing aan.
Een ontbrekend vereist argument kan ertoe leiden dat een programma gebruiksinformatie afdrukt en een foutstatus anders dan nul teruggeeft.
argc
De parameter argc geeft het aantal argumentstrings dat aan main is doorgegeven, normaal inclusief de string waarmee het programma is gestart.
./label red blue uitvoeren in een gewone shell geeft argc gelijk aan 3: de startnaam en twee kleuren.
argv
De parameter argv geeft via indexen toegang tot argumentstrings, gevolgd door een nulpointer op de positie die argc aangeeft.
Na controle van argc kan een programma strcmp(argv[1], "-v") gebruiken om de eerste optie te herkennen.
Array
Een array is een reeks naast elkaar gelegen elementen van één type met een vaste grootte, die je via een gedeelde naam en een geheeltallige index benadert.
int levels[6] reserveert zes gehele getallen; als je levels[2] wijzigt, verandert de derde opgeslagen waarde.
Array met variabele lengte
Een array met variabele lengte krijgt zijn grootte wanneer de declaratie wordt uitgevoerd en behoudt die grootte tot het einde van zijn levensduur.
In C17 maakt een lokale const int n = 6; int a[n]; een array met variabele lengte omdat n geen constante expressie is.
Array van stringpointers
Een array van stringpointers slaat per string één verwijzing op, zodat elders opgeslagen teksten verschillende lengtes kunnen hebben.
const char *names[] = {"Al", "Mina"} slaat twee verwijzingen op en maakt het mogelijk een verwijzing te vervangen.
Arrayparameter
Een arrayparameter wordt omgezet naar een pointerparameter en geeft een functie toegang tot elementen van de aanroepende functie zonder hun aantal.
Een functie die met int a[] is gedeclareerd, heeft nog steeds een afzonderlijke lengte nodig voordat ze haar invoer kan doorlopen.
ASCII
ASCII is een tekencodering die de waarden 0 tot en met 127 toekent aan basisletters, cijfers, leestekens en besturingstekens.
De berekening '8' - '0' levert 8 op omdat codes van cijfertekens opeenvolgend zijn.
atoi
De functie atoi converteert een numeriek begindeel naar int zonder betrouwbare foutmelding en heeft ongedefinieerd gedrag bij overloop.
De invoer “18x” levert met atoi 18 op, dus een teruggegeven getal bewijst niet dat de volledige invoer numeriek was.

B

Bestandseinde
Bestandseinde geeft aan dat geen verdere invoer beschikbaar is; fgets kan nog steeds slagen als de functie tekens heeft gelezen voordat die toestand werd bereikt.
Een bestand dat eindigt met “done” zonder nieuweregelteken laat een voldoende grote fgets-buffer nog steeds die laatste tekst ontvangen.
Bitmasker
Een bitmasker selecteert bepaalde bitposities voor een test; een AND-bewerking behoudt alleen de invoerbits die in het masker met enen zijn gemarkeerd.
Het masker 0xC0 behoudt de twee hoogste bits zodat een programma het UTF-8-vervolgpatroon kan herkennen.
Buffer
Een buffer is een opslaggebied dat is gereserveerd voor gegevens die worden gelezen, geschreven of getransformeerd, met een capaciteit die geldige schrijfbewerkingen begrenst.
Een naambuffer van 32 bytes kan dertig ASCII-teksttekens, een behouden nieuweregelteken en een afsluitende nulbyte bevatten.
Bufferoverloop
Een bufferoverloop is een schrijfbewerking voorbij de beschikbare opslag van een buffer die nabijgelegen gegevens of besturingsinformatie van het programma kan beschadigen.
Zes tekstbytes en een afsluitend nulteken naar een array van zes bytes kopiëren overschrijdt die array met één byte.

C

C-string
Een C-string is een reeks tekens die eindigt bij een nulteken, waarbij de tekst in een array wordt opgeslagen in plaats van in een afzonderlijk ingebouwd stringtype.
De array die wordt geïnitialiseerd door char pet[] = "dog" bevat drie letters gevolgd door een teken met waarde nul.
Caesarverschuiving
Een Caesarverschuiving vervangt elke letter door een andere op een vast aantal alfabetposities afstand en loopt daarbij binnen het letterbereik terug naar het begin.
Bij ASCII-tekst geeft een verschuiving van hoofdletter Y met twee de letter A, terwijl een cijfer of komma ongewijzigd kan blijven.
Capaciteit
De capaciteit van een array is het aantal elementen dat in de toegewezen opslag past, ongeacht hoeveel daarvan op dit moment bruikbare waarden bevatten.
Een array met capaciteit 80 en aantal 29 heeft ruimte voor 51 extra waarden als het aantal correct wordt bijgehouden.

D

Deelstring
Een deelstring is een aaneengesloten reeks tekens binnen een andere string; strstr vindt de eerste overeenkomende beginlocatie.
Als je in “blue-green” naar “green” zoekt, krijg je een locatie vanaf waar de resterende string “green” luidt.

E

Eén-ernaastfout
Een één-ernaastfout gebruikt een grens die één positie te ver of te kort is, vaak doordat het aantal elementen wordt verward met de laatste geldige index.
De voorwaarde i <= n laat een doorloop van n elementen vanaf index nul één extra toegang op index n proberen.
Eindpointer
Een eindpointer legt vast waar de omzetting van tekst naar een getal stopte, zodat een programma kan detecteren dat geen conversie plaatsvond of dat ongewenste tekens overbleven.
Voor invoer “9z” geeft strtol 9 terug, maar verwijst de eindpointer naar de z die niet is verwerkt.
Element
Een element is één waarde die in een array is opgeslagen, met hetzelfde gedeclareerde type als de andere waarden en een eigen positie met een index.
In int a[3] = {8, 1, 6} bevat element a[1] de waarde 1 en kun je er een ander geheel getal aan toewijzen.
ERANGE
De macro ERANGE duidt een bereikfout aan die via errno wordt gemeld, zoals een strtol-resultaat dat te groot of te klein is voor long.
Zet errno vóór de conversie op nul en test daarna op ERANGE voordat je een lange reeks cijfers accepteert.
Escapesequentie
Een escapesequentie is een schrijfwijze in de broncode met een backslash voor een teken dat speciale notatie nodig heeft, zoals een nieuweregelteken of een aanhalingsteken.
De literal "A\nB" bevat een nieuweregelteken tussen de letters; de backslash en n coderen één teken.

F

fgets
De functie fgets leest begrensde regelinvoer, behoudt een bereikt nieuweregelteken en voegt een afsluitend nulteken toe wanneer ze met succes tekst leest.
Een buffer van acht bytes kan hoogstens zeven invoertekens ontvangen, waardoor extra regeltekst voor latere leesbewerkingen overblijft.
Filteren
Filteren bouwt een resultaat op uit geselecteerde invoertekens en gebruikt een afzonderlijke schrijfpositie omdat afgewezen tekens geen uitvoerruimte innemen.
Als je alleen letters uit “q2r7” behoudt, schrijf je “qr” naar de uitvoer en voeg je daarna de afsluitende nulbyte toe.
Frequentiearray
Een frequentiearray slaat voor elke categorie één teller op en gebruikt een gevalideerde categoriewaarde om de te verhogen teller te selecteren.
Na omzetting van een cijfer naar waarde 4 voegt counts[4]++ één voorkomen toe aan de telling voor dat cijfer.

G

Geneste lussen
Geneste lussen plaatsen één lus binnen een andere, zodat de binnenste lus voor elke waarde van de buitenste lus wordt herhaald, zoals kolommen binnen elke rij.
Een doorloop van twee rijen en vijf kolommen voert de binnenste lusinhoud tien keer uit wanneer beide indexen binnen hun grenzen blijven.

I

Index
Een index is een geheeltallige positie waarmee je een arrayelement selecteert, vanaf nul tot één minder dan het aantal elementen.
Bij een array met zeven elementen benadert index 6 het laatste element; index 7 ligt er al buiten.
Initialisator
Een initialisator geeft beginwaarden wanneer een object wordt gedeclareerd; ontbrekende waarden in een arrayinitialisator worden nul.
int hits[4] = {3} initialiseert de eerste teller op 3 en de andere drie tellers op nul.

K

Kolom
Een kolom groepeert dezelfde positie binnen de rijen van een tabel; de kolomindex is de tweede index in g[r][c].
Om kolom 2 op te tellen, houd je c gelijk aan 2 en varieer je r over elke rij terwijl je g[r][c] leest.

L

Lege string
Een lege string bevat geen teksttekens en wordt voorgesteld door een afsluitend nulteken op de eerste positie, waardoor de lengte nul is.
char message[20] = "" biedt een geldige lege doelstring om later tekst toe te voegen die binnen de capaciteit past.
Lineair zoeken
Bij lineair zoeken controleer je elementen op volgorde totdat je een doel vindt of elke kandidaat zonder overeenkomst hebt onderzocht.
Zoeken naar 2 in {3, 8, 2} slaagt op index 2 nadat de eerdere ongelijke elementen zijn gecontroleerd.
Locale
Een locale levert conventies die bibliotheekfuncties gebruiken; de standaardlocale “C” geeft classificatie per byte de basisregels voor tekens.
Bytegewijze toupper in de locale “C” zet een volledige UTF-8-letter met accent niet om in de hoofdlettervorm.

M

memcmp
De functie memcmp vergelijkt een vast aantal bytes en meldt hun volgorde. Ze test opslagrepresentaties in plaats van numerieke waarden.
Positieve en negatieve drijvendekommanul kunnen als waarden gelijk zijn terwijl memcmp verschillende opgeslagen bytes vindt.
memcpy
De functie memcpy kopieert precies een gevraagd aantal bytes tussen niet-overlappende gebieden zonder afsluitende stringtekens te interpreteren.
memcpy(b, a, sizeof a) kopieert de volledige opslag van array a wanneer b genoeg ruimte heeft en een afzonderlijk gebied is.
memmove
De functie memmove kopieert een gevraagd aantal bytes met gedefinieerd gedrag, ook wanneer bron- en doelgebied overlappen.
Drie bytes binnen dezelfde tekenarray verplaatsen vereist een bewerking die overlap veilig verwerkt, zoals memmove.
memset
De functie memset vult een gebied met een herhaalde bytewaarde zonder die numerieke waarde afzonderlijk aan elk arrayelement toe te wijzen.
memset(counts, 0, sizeof counts) zet een array van gehele getallen op nul; bytewaarde 1 zou niet het gehele getal 1 toewijzen.
Modulaire terugloop bij gehele getallen zonder teken
Modulaire terugloop bij gehele getallen zonder teken reduceert een rekenresultaat zonder teken modulo het bereik van het type, zodat één aftrekken van nul een grote waarde geeft.
Een aftelling met size_t kan niet stoppen doordat de waarde negatief wordt; gebruik i > 0 en benader positie i - 1.

N

Nulpointer
Een nulpointer is een pointerwaarde die naar geen object verwijst; zoekfuncties gebruiken die om aan te geven dat geen overeenkomende locatie bestaat.
Controleer strchr(name, '.') != NULL voordat je het resultaat gebruikt als begin van het achterste deel van een bestandsnaam.

O

Omzetting van tekst naar een getal
Een omzetting van tekst naar een getal interpreteert tekst als een getal en vereist afzonderlijke controles op toegestane schrijfwijze, verwerkte tekst en toegestaan waardenbereik.
De bytes “27” moeten worden geconverteerd voordat een programma de numerieke waarde met % 26 kan reduceren.
Ongedefinieerd gedrag
Ongedefinieerd gedrag betekent dat de C-standaard geen eisen stelt aan het resultaat van een bewerking, zodat geen bepaalde uitkomst is gegarandeerd.
Een ongeldige schrijfbewerking naar een array kan in de ene versie onschuldig lijken en in een andere versie vastlopen of gegevens beschadigen.
Onvolledige regel
Een onvolledige regel is het deel dat is gelezen voordat de groottelimiet van een buffer de invoer stopt, waardoor mogelijk meer van die logische regel ongelezen blijft.
Een lang antwoord kan in twee fgets-delen aankomen, dus mag het tweede deel geen nieuw antwoord worden.
Opdrachtregelargument
Een opdrachtregelargument is een string die wordt meegegeven wanneer een programma start, vaak door een shell gegroepeerd of uitgebreid voordat het programma die ontvangt.
Door “North Hall” op de opdrachtregel tussen aanhalingstekens te zetten, kun je één argument met een spatie meegeven in plaats van twee woorden.

P

Parameter met const-kwalificatie
Een parameter met const-kwalificatie voorkomt dat je de elementen waarnaar wordt verwezen via die parameter wijzigt, terwijl je ze nog wel kunt lezen.
Een functie die een totaal berekent, gebruikt const int a[] omdat de invoerwaarden optellen geen wijziging ervan vereist.
Parameter met een variabel gemodificeerd type
Een parameter met een variabel gemodificeerd type gebruikt eerdere grootteparameters in het arraytype, zodat een functie tabellen met verschillende rijbreedtes kan ontvangen.
GCC en Clang accepteren int g[rows][cols] na parameters die positieve, overeenkomende aantallen rijen en kolommen definiëren.

R

Rij
Een rij is één binnenste array in een tweedimensionale array, met een vast aantal opeenvolgende elementen van hetzelfde type.
In int table[2][5] duidt table[1] de tweede rij aan, die vijf posities voor gehele getallen bevat.
Rijbreedte
De rijbreedte is het aantal elementen in elke binnenste array en is bij een tabelparameter nodig om elementen via hun index te vinden.
De parameter int g[][6] accepteert rijen van zes gehele getallen breed, terwijl de aanroepende functie het aantal rijen afzonderlijk doorgeeft.
Rijgewijze opslagvolgorde
Rijgewijze opslagvolgorde slaat alle elementen van één rij aaneengesloten op vóór de volgende rij, zoals vereist voor meerdimensionale arrays in C.
Het element na grid[0][3] in de opslag van een tabel met vier kolommen is grid[1][0].

S

Scheidingsteken
Een scheidingsteken is een teken dat delen van tekst scheidt, zoals de komma of spatie die aan een tokenisatiefunctie wordt opgegeven.
Met scheidingstekens “, ” kan de tekst “one, two” worden gesplitst in de tokens “one” en “two”.
size_t
Het type size_t is een geheelgetaltype zonder teken dat wordt gebruikt voor objectgroottes en door sizeof wordt teruggegeven; het wordt vaak gebruikt voor arraylengtes.
printf("%zu", sizeof data) gebruikt het formaat voor groottes om het aantal bytes af te drukken dat data inneemt.
snprintf
De functie snprintf maakt tekst op binnen een opgegeven capaciteit en geeft de volledig benodigde tekstlengte of een negatief foutresultaat terug.
Een retourwaarde van 12 bij capaciteit 10 betekent dat het opgeslagen resultaat is afgekapt, ook al heeft het nog een afsluitend nulteken.
strcat
De functie strcat voegt een bronstring toe bij het bestaande afsluitende nulteken van een doelstring en schrijft na de gecombineerde tekst een nieuw afsluitend nulteken.
“box” toevoegen aan “red ” vereist acht bytes doelopslag, inclusief de afsluitende nulbyte.
strchr
De functie strchr vindt het eerste overeenkomende teken in een string en geeft de locatie ervan terug, of een nulpointer als er geen overeenkomst is.
Met strchr naar een punt zoeken in “name.part.ext” vindt het achterste deel “.part.ext”, inclusief de eerste punt.
strcmp
De functie strcmp vergelijkt strings met afsluitend nulteken als bytes zonder teken en geeft afhankelijk van hun volgorde een negatief resultaat, nul of een positief resultaat terug.
strcmp("oak", "oak") == 0 test gelijke inhoud, ook wanneer de twee strings afzonderlijke opslag innemen.
strcpy
De functie strcpy kopieert een volledige bronstring en het afsluitende nulteken naar een niet-overlappend doel met voldoende beschikbare opslag.
“pine” kopiëren schrijft vijf bytes inclusief nul, dus past het in een doelarray gedeclareerd als char out[5].
strcspn
De functie strcspn meet het eerste deel van een string dat geen van de tekens bevat die in het tweede argument zijn opgegeven.
strcspn("fir:oak", ":,") geeft 3 terug omdat het eerste opgegeven scheidingsteken na de drie letters van fir staat.
Stringlengte
De stringlengte is het aantal tekstbytes vóór het eerste nulteken, zonder dat teken en ongebruikte capaciteit mee te tellen.
Een array van 40 bytes met “June” heeft lengte 4, hoewel de beschikbare opslag 40 bytes blijft.
Stringlijst met vaste rijbreedte
Een stringlijst met vaste rijbreedte slaat tekst op in rijen van een 2D-char-array, waarbij elke rij een eigen vaste capaciteit en ruimte voor een afsluitend nulteken reserveert.
In char labels[5][12] heeft elk label twaalf bytes opslagruimte, ook wanneer de huidige tekst korter is.
Stringliteral
Een stringliteral is tekst tussen dubbele aanhalingstekens die een tekenarray met afsluitend nulteken oplevert waarvan het draaiende programma de inhoud niet mag wijzigen.
const char *label = "ready" verwijst naar een literal; een beschrijfbare kopie kan char label[] = "ready" gebruiken.
strlen
De functie strlen doorzoekt een geldige string tot het afsluitende nulteken en geeft het aantal voorafgaande bytes terug als een size_t-waarde.
Voor char tag[30] = "oak" is strlen(tag) gelijk aan 3, terwijl sizeof tag de volledige capaciteit van 30 bytes meldt.
strncat
De functie strncat voegt hoogstens een opgegeven aantal tekstbytes uit de bron toe en schrijft daarna een afsluitend nulteken.
Voor een geldige doelstring is de resterende tekstruimte de capaciteit min de huidige lengte min één.
strncmp
De functie strncmp vergelijkt hoogstens een opgegeven aantal stringbytes en stopt eerder bij een einde of verschil.
Een overeenkomst van drie bytes tussen “run-fast” en “run-slow” stelt een gedeeld beginstuk vast, geen gelijke volledige strings.
strncpy
De functie strncpy kopieert tot een grens en vult kortere tekst aan met nullen, maar schrijft geen afsluitend nulteken als de bron die grens vult.
“abcd” kopiëren met grens 3 slaat a, b en c op zonder nulbyte, dus moet een volgende stap het resultaat afsluiten.
strrchr
De functie strrchr vindt het laatste overeenkomende teken in een string en geeft de locatie ervan terug, of een nulpointer als er geen overeenkomst is.
De laatste punt in “name.part.ext” begint “.ext”, dus kan strrchr het scheidingsteken van de extensie vinden.
strtol
De functie strtol converteert tekst naar long met een gekozen grondtal en rapporteert een stoplocatie en informatie over bereikfouten.
Met strtol(text, &end, 10) kan het programma een overgebleven letter na verder geldige decimale cijfers afwijzen.

T

Tekenclassificatie
Tekenclassificatie test met functies in <ctype.h> of een tekencode tot een categorie behoort, zoals cijfers, letters of witruimte.
isdigit((unsigned char) c) geeft een resultaat anders dan nul wanneer de opgeslagen byte een decimaal cijfer voorstelt.
Tekencodering
Een tekencodering legt de koppeling vast tussen tekst en de opgeslagen numerieke representatie, inclusief de bytes voor verzending of opslag.
Een C-string met hetzelfde zichtbare woord kan bij verschillende coderingen verschillende aantallen bytes nodig hebben.
Tekenconstante
Een tekenconstante is een C-expressie tussen enkele aanhalingstekens met type int die een tekencode voorstelt, zoals de code voor een letter.
Met ASCII-codes toont printf("%c %d", 'B', 'B') de letter B en de numerieke waarde 66.
Toegang buiten de grenzen
Toegang buiten de grenzen leest of schrijft een positie met een index buiten de daadwerkelijke array of rij die wordt benaderd en heeft ongedefinieerd gedrag.
a[8] benaderen in een array met acht elementen is ongeldig, ook als een testuitvoering een bruikbare waarde lijkt terug te geven.
Toegestaan waardenbereik van de toepassing
Het toegestane waardenbereik van de toepassing is het interval van waarden dat een programma voor een doel toestaat en kan smaller zijn dan het volledige bereik van het numerieke type.
Een geconverteerd aantal van 120 past in een geheel getal, maar voldoet niet aan een programmaregel die een aantal van 1 tot en met 100 vereist.
Tokenisatie
Tokenisatie verdeelt tekst in delen die door scheidingstekens zijn gescheiden; strtok doet dit door de invoer te wijzigen en een positie te onthouden.
Een tweede aanroep strtok(NULL, ",") gaat verder met het splitsen van dezelfde beschrijfbare invoer in plaats van opnieuw te beginnen.
Transformatie op dezelfde geheugenlocatie
Een transformatie op dezelfde geheugenlocatie wijzigt waarden in hun bestaande opslag in plaats van een afzonderlijke uitvoerarray op te bouwen.
Bij het omzetten van een beschrijfbare ASCII-string naar hoofdletters kun je elk geconverteerd teken terugplaatsen op dezelfde positie met een index.
Tweedimensionale array
Een tweedimensionale array is een array van rijarrays met gelijke breedte, waarbij twee indexen één element op rij en kolom selecteren.
int board[4][6] biedt 24 gehele getallen, bereikbaar via vier geldige rij-indexen en zes geldige kolomindexen.

U

Uitvoerarray
Een uitvoerarray is opslag die een aanroepende functie beschikbaar stelt zodat een functie meerdere resultaten kan invullen, met een afgesproken capaciteit of aantal.
Een functie die kwadraten berekent, kan elk resultaat in out[i] schrijven terwijl het eigenaarschap van de array bij de aanroepende functie blijft.
Unicode-codepunt
Een Unicode-codepunt is een genummerde positie in de coderuimte van Unicode, waarmee tekens en andere toegekende tekstwaarden worden aangeduid.
De vooraf samengestelde letter “é” is U+00E9, ook al bestaat de UTF-8-opslag uit twee afzonderlijke bytes.
UTF-8
UTF-8 is een Unicode-codering die elke scalaire waarde in één tot vier bytes opslaat en ASCII-coderingen van één byte behoudt.
De string “café” neemt vijf UTF-8-tekstbytes in, plus één nulbyte wanneer die als C-string wordt opgeslagen.

V

Veldbreedte
Een veldbreedte in een %s-conversie begrenst het aantal invoertekens dat wordt opgeslagen vóór de extra afsluitende nulbyte.
Voor char word[10] reserveert de conversie %9s de tiende beschikbare byte voor het afsluitende nulteken.
Vergelijking op bytewaarden
Een vergelijking op bytewaarden ordent strings op opgeslagen bytewaarden zonder teken. Die volgorde kan verschillen van de alfabetische conventies van een taal.
In UTF-8 plaatst strcmp een Latijnse letter met accent na ASCII-z omdat de beginbyte een hogere waarde heeft.
Vervolgbyte
Een UTF-8-vervolgbyte heeft het voorste bitpatroon 10 en volgt op een beginbyte binnen een codepunt dat met meerdere bytes is gecodeerd.
De byte 0xA9 vervolgt de reeks voor “é”; die mag niet afzonderlijk als een volledig UTF-8-teken worden bewaard.

W

Woordgrens
Een woordgrens is de overgang waarmee een programma bepaalt waar een woord begint of eindigt, volgens de gekozen definitie van een woord.
Een teller voor letterreeksen begint een nieuw woord wanneer die een letter ziet na een spatie, leesteken of het begin van de string.

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