Arrays und Zeichenketten in C
AnfängerEinheit 3 von 8 in Kurs: C-Programmierung – Zeiger, Speicherverwaltung und sicheres C
Was du lernst
- Deklariere Arrays mit fester Größe, greife über den Index auf jedes Element zu, durchlaufe sie, ohne ihr Ende zu überschreiten, und übergib sie an Funktionen.
- Ein Array deklarieren, initialisieren und über Indizes darauf zugreifenErstelle Arrays mit einer festen Anzahl von Elementen, gib ihnen Anfangswerte und lies oder ändere einzelne Elemente über ihren Index.
- Arrays mit Schleifen durchlaufen, ohne ihre Grenzen zu überschreitenGreife mit einer Zählschleife auf jedes Element zu und verwende dieses Muster, um zu summieren, den größten Wert zu finden, zu suchen, zu zählen und die Reihenfolge umzukehren.
- Arrays an Funktionen übergebenSchreibe Funktionen, die Arrays als Parameter entgegennehmen, übergib die Länge separat und erkenne, wann die Funktion die Daten des Aufrufers ändern kann.
- Ein Array deklarieren, initialisieren und über Indizes darauf zugreifen
- Behandle Zeichen als kleine ganzzahlige Codes, speichere Text in
char-Arrays, die mit einem Nullzeichen enden, verarbeite ihn Zeichen für Zeichen und sieh dir an, wie UTF-8-Text in Bytes gespeichert wird.- Zeichen als numerische Codes behandelnVerwende den Typ
charund Zeichenkonstanten als Zahlen, wandle Ziffern und Buchstaben rechnerisch um und klassifiziere Zeichen mit<ctype.h>. - Text als nullterminierte Zeichenketten speichernErkläre, wie eine C-Zeichenkette in einem
char-Array aufgebaut ist, warum sie ein abschließendes Nullzeichen benötigt und wie sich Zeichenkettenliterale und Arrays unterscheiden. - Zeichenketten zeichenweise verarbeitenDurchlaufe eine Zeichenkette bis zu ihrem abschließenden Nullzeichen, um Text zu zählen, umzuwandeln, umzukehren, zu filtern und zu verschlüsseln, und schließe jede neue Zeichenkette mit
'\0'ab. - Erklären, wie UTF-8-Text in char-Arrays gespeichert wirdSetze Zeichen, Unicode-Codepunkte und UTF-8-Bytes zueinander in Beziehung und sage voraus, was bytebasierte C-Funktionen mit Text außerhalb von ASCII tun.
- Zeichen als numerische Codes behandeln
- Bestimme mit
<string.h>die Länge von Zeichenketten, kopiere, verknüpfe, vergleiche und durchsuche sie und stelle sicher, dass jedes Ziel Platz für das Ergebnis und dessen abschließendes Nullzeichen hat.- Die Länge von Zeichenketten bestimmen, Zeichenketten kopieren und verkettenVerwende
strlen,strcpyundstrcatkorrekt und ermittle, wie viel Platz jedes Ziel benötigt. - Zeichenketten vergleichen und durchsuchenVergleiche Zeichenketten mit
strcmpundstrncmp, finde Zeichen und Teilzeichenketten und zerlege Text in Token. - Beim Kopieren Speichergrenzen einhalten und mit rohem Speicher arbeitenVerwende die größenbegrenzten Funktionen
strncpy,strncatundsnprintfkorrekt, erkenne abgeschnittenen Text und kopiere, fülle und vergleiche ganze Arrays mit den Speicherfunktionen.
- Die Länge von Zeichenketten bestimmen, Zeichenketten kopieren und verketten
- Speichere Tabellen und Zeichenkettenlisten in Arrays mit zwei oder mehr Dimensionen, durchlaufe sie mit verschachtelten Schleifen und übergib sie an Funktionen.
- Zweidimensionale Arrays deklarieren und durchlaufenDeklariere und initialisiere Wertetabellen, erkläre die Anordnung ihrer Zeilen im Speicher und verarbeite Zeilen und Spalten mit verschachtelten Schleifen.
- Tabellen an Funktionen übergeben und Zeichenkettenlisten speichernSchreibe Funktionen, die zweidimensionale Arrays entgegennehmen, und speichere mehrere Zeichenketten entweder in einem 2D-
char-Array oder in einem Array von Zeichenkettenzeigern.
- Zweidimensionale Arrays deklarieren und durchlaufen
- Lies die nach einem Programmnamen eingegebenen Wörter über
argcundargv, prüfe ihre Anzahl und wandle sie sicher in Zahlen und Optionen um.- argc und argv auslesenDeklariere
mainmit seinen beiden Parametern, beschreibe den Inhalt jedes Elements vonargvund prüfe die Anzahl der Argumente, bevor du ein Argument verwendest. - Numerische Argumente konvertieren und auf Zulässigkeit prüfenWandle Argumentzeichenketten mit Fehlerprüfung in Zahlen um, weise unzulässige Eingaben zurück und prüfe Werte vor ihrer Verwendung auf den zulässigen Wertebereich.
- argc und argv auslesen
- Erkenne, wie C-Programme außerhalb ihrer Arrays lesen und schreiben, warum das gefährlich ist und wie du Text so liest und kopierst, dass er immer in den vorgesehenen Speicher passt.
- Zugriffe außerhalb der Speichergrenzen und ihre Folgen erkennenErkläre, warum C Zugriffe außerhalb der Speichergrenzen nicht verhindert, was dabei geschehen kann und welche Fehler dazu führen.
- Text unter Einhaltung der Puffergrenzen lesen und kopierenLies Eingaben mit Größenbegrenzung, erkenne zu lange Zeilen und kopiere Text nur, wenn er in das Ziel passt.
- Zugriffe außerhalb der Speichergrenzen und ihre Folgen erkennen
Arrays und Zeichenketten in C einfach erklärt
1Wertesammlungen in Arrays speichern und verarbeiten
Mit Arrays kann eine Schleife viele zusammengehörige Werte verarbeiten. Das Array legt fest, wie viel Speicher vorhanden ist, während Indizes einzelne Elemente auswählen und eine Anzahl festhält, wie viele belegt sind. Lerne, wie Deklarationen Anfangswerte festlegen, wie Schleifen Ergebnisse berechnen, ohne eine Grenze zu überschreiten, und wie Funktionen auf die Elemente des Aufrufers zugreifen. Kleine Beispiele zeigen, welche Positionen der Code genau liest oder beschreibt.
1.1 Ein Array deklarieren, initialisieren und über Indizes darauf zugreifen
Ein Array gibt einer festen Anzahl von Werten desselben Typs einen Namen. Seine Elemente liegen im Speicher nebeneinander. Verwende ein Array, wenn dieselbe Operation auf viele Werte angewendet wird, etwa beim Aktualisieren einer Reihe von Punktzahlen.
Die Elemente erstellen
int scores[5] = {90, 72, 85, 64, 99};
int primes[] = {2, 3, 5, 7};
int counts[6] = {0};
Die erste Deklaration erstellt fünf Ganzzahlen. Der Compiler zählt vier Werte im zweiten Initialisierer, sodass primes vier Elemente hat. Im dritten ist das erste Element ausdrücklich null und jeder fehlende Initialisierungswert wird ebenfalls null. Ebenso enthält int a[5] = {1, 2}; die Werte 1, 2, 0, 0, 0. Ein seit C99 verfügbarer designierter Initialisierer wählt eine Position: int b[5] = {[2] = 7}; setzt das dritte Element auf 7 und die anderen auf null.
Eine lokale Deklaration mit automatischer Speicherdauer wie int data[5]; reserviert Platz, ohne Anfangswerte bereitzustellen. Beschreibe jedes Element, bevor du es liest. Arrays, die außerhalb aller Funktionen deklariert werden, und Arrays, die als static deklariert werden, werden mit null initialisiert, wenn kein expliziter Initialisierer angegeben ist.
Über den Index auf einen Wert zugreifen
Indizes beginnen bei null. Bei fünf Elementen sind die Positionen 0 bis 4 zulässig. scores[1] bezeichnet die zweite Punktzahl, nicht die erste. Ein Element kann überall dort verwendet werden, wo eine gewöhnliche Variable seines Typs verwendet werden kann:
scores[1] = 75;
scores[1] += 5;
int last = scores[4];
Die zweite Punktzahl ist jetzt 80. Die letzte ist weiterhin 99. Ein Index kann eine Variable oder ein Ausdruck sein. Deshalb greift scores[n - 1] auf das letzte Element zu, wenn n die Anzahl der Elemente und positiv ist. Bei scores[5] gibt es kein Element, auf das zugegriffen werden könnte.
Elemente und Bytes zählen
sizeof scores misst das gesamte Array in Bytes. Auf einem System mit 4-Byte-Ganzzahlen belegen die fünf Elemente 20 Bytes. Durch Division durch die Größe eines Elements entfällt die Abhängigkeit von der Ganzzahlgröße:
size_t n = sizeof scores / sizeof scores[0];
printf("%zu\n", n);
Dies gibt 5 aus. size_t ist der vorzeichenlose Ganzzahltyp für Größen; %zu gibt ihn aus. Diese Berechnung funktioniert dort, wo scores das tatsächliche Array bezeichnet. Ein Funktionsparameter enthält nicht die vollständige Größe des Arrays.
Größe und Werte trennen
Ein Name wie #define NUM_SCORES 5 ermöglicht es, in der Deklaration und ihren Schleifen dieselbe Größe zu verwenden. In C17 erzeugt ein lokales const int n = 5; int a[n]; dagegen ein Array variabler Länge: const macht n nicht zu einem ganzzahligen konstanten Ausdruck. Seine Größe wird bei der Ausführung der Deklaration festgelegt und bleibt während der Lebensdauer dieses Arrays unverändert. Es kann keine Initialisierungsliste verwenden. Die Unterstützung für Arrays variabler Länge ist in C11 und C17 optional; C23 ergänzt constexpr für Konstanten zur Übersetzungszeit.
Arrays können nicht als Ganzes zugewiesen werden: b = a; ist ungültig. Kopiere ihre Elemente mit einer Schleife oder einer geeigneten Speicherfunktion. Bei zwei getrennten Arrays vergleicht a == b Adressen, nicht Elementwerte. Vergleiche einander entsprechende Elemente, um die Gleichheit der Werte zu prüfen. Ein byteweiser Vergleich mit memcmp prüft dagegen ihre gespeicherten Darstellungen; gleiche Gleitkommawerte wie positive und negative null können unterschiedliche Darstellungen haben.
Wichtige Erkenntnisse
- Arrayindizes reichen von 0 bis eins weniger als die Anzahl der Elemente.
- Fehlende Initialisierungswerte werden null; nicht initialisierte lokale Elemente müssen vor ihrer Verwendung beschrieben werden.
- Verwende
sizeof a / sizeof a[0]nur dort, woaein tatsächliches Array ist. - Ein Array behält seine Größe während seiner Lebensdauer.
- Kopiere und vergleiche Arraywerte Element für Element.
1.2 Arrays mit Schleifen durchlaufen, ohne ihre Grenzen zu überschreiten
Eine Zählschleife verbindet die Positionen eines Arrays mit einer Operation. Entscheide, welche Elemente nutzbare Daten enthalten, und greife dann genau auf diese Positionen zu. Dasselbe Muster eignet sich für Summen, Suchvorgänge und Aktualisierungen.
Auf die belegten Elemente zugreifen
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;
Die Schleife verwendet die Indizes 0, 1, 2, 3 und 4. Die Summe nimmt nacheinander die Werte 12, 27, 36, 50 und 60 an, der Mittelwert ist also 12,0. Mit i <= 5 käme ein unzulässiger Zugriff auf Index 5 hinzu. Dieser zusätzliche Durchlauf ist ein Off-by-one-Fehler.
Dividiere für einen Mittelwert mit Gleitkommaarithmetik. Bei einer Summe von 61 ergibt 61 / 5 die Ganzzahl 12; 61 / 5.0 ergibt 12,2. Wird das erste Ergebnis anschließend in einem double gespeichert, lässt sich der bereits abgeschnittene Nachkommateil nicht wiederherstellen.
Ein Maximum oder einen Treffer finden
Initialisiere bei einem nicht leeren Array das Maximum mit seinem ersten Wert:
int max = t[0];
for (int i = 1; i < 5; i++) {
if (t[i] > max) max = t[i];
}
Der erste Vergleich ersetzt 12 durch 15. Die übrigen Werte sind kleiner, sodass 15 bleibt. Ein Startwert von null würde bei einer Liste wie {-8, -3, -11} scheitern: null ist größer als jeder gespeicherte Wert, steht aber nicht in der Liste.
Eine lineare Suche prüft Werte der Reihe nach:
int found = -1;
for (int i = 0; i < 5; i++) {
if (t[i] == 14) {
found = i;
break;
}
}
Hier wird found zu 3. Der Wert -1 markiert, dass es keinen Treffer gibt, weil er kein zulässiger Index sein kann. Melde erst dann einen Misserfolg, wenn alle Kandidaten geprüft wurden. Wird -1 nach dem ersten abweichenden Element zurückgegeben, werden spätere Treffer im Array übersehen.
Zählen und umordnen
Ein Häufigkeitsarray speichert einen Zähler pro Kategorie. Für Ziffern stellt int counts[10] = {0}; die Zähler 0 bis 9 bereit. Nach der Prüfung von d >= 0 && d < 10 erhöht counts[d]++ den Zähler für die Ziffer d. Die Prüfung schützt das Array, wenn der Ziffernwert aus einer Eingabe stammt.
Um ein Array umzukehren, tausche Paare von seinen Enden her:
for (int i = 0; i < 5 / 2; i++) {
int temp = t[i];
t[i] = t[5 - 1 - i];
t[5 - 1 - i] = temp;
}
Der erste Tausch vertauscht 12 und 10. Der zweite vertauscht 15 und 14. Die 9 in der Mitte bleibt stehen, sodass {10, 14, 9, 15, 12} entsteht. Höre nach der Hälfte auf: Würdest du alle fünf Positionen durchlaufen, würde jedes Paar zweimal getauscht.
Anzahl und Kapazität getrennt festhalten
Ein Array mit Platz für 100 Werte kann derzeit nur 37 nutzbare Einträge enthalten. Seine Kapazität ist 100; die Anzahl belegter Elemente ist 37. Verarbeite Positionen unterhalb von count. Um einen Wert anzuhängen, prüfe zuerst count < CAPACITY, speichere ihn bei a[count] und erhöhe dann count. Eine Schleife bis zur Kapazität kann Positionen lesen, die noch nie nutzbare Werte erhalten haben, obwohl diese Positionen physisch innerhalb des Arrays liegen.
Wichtige Erkenntnisse
- Verwende eine strikte Schleifenschranke mit
<. - Verwende Gleitkommadivision, wenn ein Mittelwert einen Nachkommateil haben kann.
- Initialisiere das Maximum eines nicht leeren Arrays mit einem Element, auch wenn alle Daten negativ sind.
- Eine Suche ist erst erfolglos, nachdem alle Kandidaten geprüft wurden.
- Prüfe einen Wert auf Zulässigkeit, bevor du ihn als Index verwendest.
- Kehre die Reihenfolge um, indem du nur die erste Hälfte der Paare tauschst.
- Die Anzahl belegter Elemente gibt an, wie viele Elemente genutzt werden; die Kapazität ist der verfügbare Platz.
1.3 Arrays an Funktionen übergeben
Eine Funktion kann ein Array verarbeiten, ohne eine separate Kopie jedes Elements zu erhalten. Der Aufruf ermöglicht den gemeinsamen Zugriff auf den Speicher des Aufrufers. Übergib die Anzahl der Elemente zusätzlich, damit die Funktion weiß, wo sie aufhören muss.
Daten und Länge bereitstellen
#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;
}
Der Aufrufer kann int values[] = {7, 8, 10}; deklarieren und average(values, sizeof values / sizeof values[0]) aufrufen. Die drei Additionen ergeben 25; die Umwandlung von sum in double vor der Division ergibt ungefähr 8,33. Die Rückgabe von 0,0 bei keinen Werten ist das für diese Funktion gewählte Verhalten bei leerer Eingabe. Die kleinen Werte in diesem Beispiel sorgen dafür, dass die ganzzahlige Summe im Wertebereich bleibt.
In einer Parameterdeklaration wird int a[] zu int *a angepasst. Der Parameter erhält die Adresse des ersten Elements. Selbst int a[10] prüft nicht, ob zehn Elemente vorhanden sind, und erzwingt dies auch nicht. Der Aufrufer muss eine Länge angeben, die zu den verfügbaren Daten passt.
Warum sizeof die Länge verliert
An der Aufrufstelle misst sizeof values das Array. Innerhalb der Funktion misst sizeof a den Zeigerparameter. Wenn Zeiger 8 Bytes und Ganzzahlen 4 Bytes belegen, ergibt die Division dieser Größen unabhängig von der Elementanzahl des Aufrufers den Wert 2. Dieses Ergebnis beschreibt weder eine Kopie mit zwei Elementen noch das ursprüngliche Array. Behalte die Länge als expliziten Parameter bei.
size_t, deklariert durch Headerdateien wie <stddef.h>, ist der von sizeof erzeugte vorzeichenlose Typ. Er kann Objektgrößen darstellen und %zu gibt ihn aus. Wird er für den Schleifenindex verwendet, vermeidet das den Vergleich eines vorzeichenbehafteten Index mit einer vorzeichenlosen Länge.
Entscheiden, ob Elemente verändert werden dürfen
Das const in const int a[] verhindert Zuweisungen an Elemente über diesen Parameter. Es eignet sich für eine Funktion, die aus den Daten ein Ergebnis berechnet. Eine Funktion, die Elemente aktualisieren soll, lässt es weg:
void zero(int a[], size_t n) {
for (size_t i = 0; i < n; i++) {
a[i] = 0;
}
}
Nach zero(values, 3) enthalten die drei Elemente des Aufrufers alle null. Das Array hat weiterhin drei Elemente. Ein skalarer Parameter wie n ist eine separate Kopie; eine Änderung dieser Kopie würde die Zählvariable des Aufrufers nicht verändern.
Um mehrere Ergebnisse zu erzeugen, lass den Aufrufer ein Ausgabearray und dessen Länge bereitstellen. Eine Funktion kann out[i] mit jedem Ergebnis befüllen. C-Funktionen können ein Array nicht als Wert zurückgeben, und die Rückgabe der Adresse eines lokalen Arrays schlägt fehl, weil dieses Array nach der Rückkehr aus der Funktion nicht mehr existiert.
Mit einem vorzeichenlosen Index rückwärts zählen
Ein vorzeichenloser Index kann nicht negativ werden. In for (size_t i = n - 1; i >= 0; i--) ist die Bedingung immer wahr; nach null springt der Wert durch Wraparound auf eine große positive Zahl. Bei n == 0 führt bereits die anfängliche Subtraktion zum Wraparound. Beginne stattdessen bei der Anzahl:
for (size_t i = n; i > 0; i--) {
printf("%d\n", a[i - 1]);
}
Bei drei Elementen sind die aufgerufenen Indizes 2, 1 und 0. Bei null Elementen wird der Schleifenrumpf nie ausgeführt. Binde <stdio.h> für printf ein.
Wichtige Erkenntnisse
- Übergib eine Arraylänge separat; aus einem Parameter lässt sich die Anzahl der Elemente nicht ermitteln.
- Schreibzugriffe über einen Arrayparameter verändern die Elemente des Aufrufers.
- Verwende
constfür einen Parameter, der Elemente nur liest. - Der Aufrufer stellt Speicher für mehrere Ausgabewerte bereit.
- Zähle bei Verwendung von
size_tvonnaus rückwärts und greife aufi - 1zu.
2Text mit Zeichen und nullterminierten Zeichenketten darstellen
Text beginnt mit Zeichencodes und endet mit einer Speicherregel: Eine C-Zeichenkette endet bei ihrem ersten Nullbyte. Lerne, wie sich Zeichen, Literale und beschreibbare Arrays unterscheiden, und verwende dann Schleifen, um Text zu zählen und umzuwandeln. UTF-8 bringt eine wichtige Unterscheidung zwischen einem Byte und einem vollständigen Codepunkt hinzu. Wenn du sowohl den Text als auch seine Speicherung nachvollziehst, verstehst du, warum ein sichtbares Wort mehr Arraypositionen benötigen kann, als seine Buchstaben vermuten lassen.
2.1 Zeichen als numerische Codes behandeln
Ein C-Zeichen wird als numerischer Code gespeichert. So kann ein Programm ein Symbol ausgeben, es mit einem anderen vergleichen oder mit seinem Code rechnen. Der Zeichensatz legt fest, welche Zahl welches Symbol darstellt.
Code und Zeichen auslesen
Ein char belegt ein Byte: sizeof(char) ist definitionsgemäß 1. Eine Zeichenkonstante wie 'A' hat den Typ int. Im hier verwendeten ASCII-basierten Zeichensatz ist ihr Code 65:
printf("%c %d\n", 'A', 'A');
Die Ausgabe ist „A 65“. %c zeigt das Zeichen an; %d zeigt seine Zahl an. ASCII vergibt die Codes 0 bis 127. Ziffern reichen von 48 bis 57, Großbuchstaben von 65 bis 90, Kleinbuchstaben von 97 bis 122, und das Leerzeichen hat den Code 32.
Einfache und doppelte Anführungszeichen erzeugen unterschiedliche Dinge. 'A' ist ein einzelner Zeichencode. "A" ist ein Array mit 'A' und einem abschließenden Nullbyte. Speichere ein einzelnes Zeichen mit char c = 'A'; und vergleiche es mit c == 'A'.
Zeichen mit Escape-Sequenzen schreiben
| C-Schreibweise | Zeichen |
|---|---|
'\n' | Zeilenumbruch, ASCII-Code 10 |
'\t' | Tabulator, ASCII-Code 9 |
'\\' | Umgekehrter Schrägstrich |
'\'' | Einfaches Anführungszeichen |
'\0' | Nullzeichen, Code 0 |
Das Nullzeichen unterscheidet sich von der sichtbaren Ziffer '0', deren ASCII-Code 48 ist. Eine Zeichenkette verwendet das Nullzeichen, um ihr Ende zu markieren.
Codes in brauchbare Werte umwandeln
Der C-Standard garantiert aufeinanderfolgende Zifferncodes. Nachdem geprüft wurde, dass c eine Ziffer ist, liefert c - '0' ihren Wert. So ist '7' - '0' gleich 7, nicht 55. Das Abziehen des Anfangscodes entfernt die Position der Ziffer innerhalb des Zeichensatzes.
Auch die ASCII-Buchstaben folgen aufeinander. Durch das Abziehen von 'A' wird ein Großbuchstabe daher in eine Position von 0 bis 25 umgewandelt. Eine Caesar-Verschiebung um drei verwendet:
char shifted = (c - 'A' + 3) % 26 + 'A';
Für 'X' ergibt das Abziehen von 65 den Wert 23; das Addieren von drei ergibt 26; der Rest nach Division durch 26 ist null; das Addieren von 65 ergibt wieder 'A'. Einfaches 'X' + 3 ergibt den Code 91, also '['. Buchstabenarithmetik hängt von einer Buchstabenreihenfolge wie in ASCII ab; C garantiert aufeinanderfolgende Ziffern, verlangt aber keine aufeinanderfolgenden Buchstaben.
Mit ctype.h klassifizieren und konvertieren
Binde <ctype.h> für isdigit, isalpha, isalnum, isspace, isupper, islower, toupper und tolower ein. Die Tests geben bei einem Treffer einen Wert ungleich null und andernfalls null zurück. toupper('q') gibt 'Q' zurück; toupper('3') lässt die Ziffer unverändert. isspace erkennt Trennzeichen wie Leerzeichen, Tabulator und Zeilenumbruch.
Ein einfaches char kann vorzeichenbehaftet sein. Ein Byte über 127 kann dann zu einem negativen Wert werden, der kein zulässiges Argument für diese Funktionen ist, es sei denn, er ist der besondere Eingabemarker EOF. Wandle bei der Verarbeitung von Bytes aus einer Zeichenkette zuerst um:
if (isalpha((unsigned char) c)) {
c = (char) toupper((unsigned char) c);
}
In der Standard-Locale „C“ erkennen diese Funktionen die grundlegenden Buchstaben, keine vollständigen mehrbyteigen UTF-8-Zeichen. Die explizite Typumwandlung macht das Byte zu einem zulässigen Argument; sie dekodiert kein UTF-8.
Wichtige Erkenntnisse
- Zeichenkonstanten verwenden einfache Anführungszeichen; Zeichenkettenliterale verwenden doppelte.
\0ist ein Zeichen mit dem Wert null, nicht die Ziffer0.- Ziehe
'0'von einem Ziffernzeichen ab, um seinen Wert zu erhalten. - Die Berechnung von Buchstabenpositionen verwendet hier die ASCII-Reihenfolge.
- Wandle ein Byte einer Zeichenkette vor einem ctype-Aufruf in
unsigned charum. - Ein ctype-Test gibt bei einem Treffer einen Wert ungleich null zurück, nicht unbedingt 1.
2.2 Text als nullterminierte Zeichenketten speichern
Eine C-Zeichenkette ist eine Folge von Zeichen, die mit '\0' endet. Das abschließende Nullzeichen ist Teil ihrer Speicherung, zählt aber nicht zu ihrer Textlänge. C hat keinen eigenen eingebauten Zeichenkettentyp: Die Zeichen belegen ein Array.
Die Bytes im Array betrachten
char word[] = "cat";
char name[20] = "Ada";
word enthält vier Bytes: 'c', 'a', 't', '\0'. Seine Arraygröße ist 4 und strlen(word) ist 3. Binde für strlen <string.h> ein. Das größere Array name enthält die drei Buchstaben, ihr abschließendes Nullzeichen und sechzehn weitere Nullbytes. Seine Kapazität ist 20, seine aktuelle Länge 3.
Ein zeichenweiser Initialisierer kann dieselbe Zeichenkette erzeugen:
char word[] = {'c', 'a', 't', '\0'};
Wird das letzte Element weggelassen, entstehen drei Zeichen ohne abschließendes Nullzeichen. C erlaubt auch char word[3] = "cat";. Das füllt alle drei Positionen und lässt das Nullbyte weg. Ein solches Array ist keine C-Zeichenkette. Eine Funktion wie strlen oder printf mit %s liest weiter, bis sie auf ein Nullbyte trifft. Sie kann dieses drei Byte große Array daher nicht sicher als Text verarbeiten.
Ein veränderbares Array oder ein Literal wählen
| Deklaration | Speicherort des Textes | Können seine Zeichen geändert werden? |
|---|---|---|
char a[] = "hello"; | Eine Kopie im Array a | Ja |
const char *p = "hello"; | Ein Zeichenkettenliteral, auf das p verweist | Nein |
a[0] = 'H'; ändert die Kopie zu „Hello“. Ein Literal darf nicht verändert werden. Selbst wenn es einem einfachen char * zugewiesen wird, ist ein Schreibzugriff über diesen Zeiger undefiniertes Verhalten. Die Schreibweise const char * macht diese Einschränkung für den Compiler sichtbar.
Der Compiler verbindet benachbarte Zeichenkettenliterale: "Hello, " "world" ergibt denselben Text wie "Hello, world". Die entstehende Zeichenkette hat weiterhin ein abschließendes Nullzeichen. Das ist nützlich, um ein langes Literal auf mehrere Quelltextzeilen aufzuteilen.
Text ausgeben und ändern
printf("%s\n", word);
printf("%c\n", word[0]);
Der erste Aufruf gibt die gesamte Zeichenkette aus. Der zweite gibt ihr erstes Zeichen aus. Ein Zeichenkettenliteral in printf steht in doppelten Anführungszeichen, weil es Text mit einem Format enthält; %c erhält einen einzelnen Zeichencode.
Eine Elementzuweisung ändert ein gespeichertes Zeichen. Bei char name[20] = "Ada"; ergibt name[0] = 'E'; den Text „Eda“. Setzt du danach name[1] = '\0';, entsteht die Zeichenkette „E“. Die Bytes für 'a' und das alte abschließende Nullzeichen stehen später im Array noch zur Verfügung, aber Zeichenkettenoperationen stoppen bei der ersten null.
Ein Array kann nach der Deklaration keinen neuen Text durch name = "Bob"; erhalten. Kopiere Zeichen mit einem geprüften strcpy-Aufruf oder einer Schleife hinein und schließe das Nullzeichen ein. Die Länge des neuen Textes muss kleiner als die Kapazität sein.
Keinen Text darstellen
Die leere Zeichenkette "" belegt dennoch ein Byte für '\0'. Sie hat die Länge null und kann mit name[0] == '\0' geprüft werden. Eine leere Zeichenkette ist eine echte Zeichenkette mit einem abschließenden Nullzeichen; ein Nullzeiger verweist dagegen auf keinen Zeichenkettenspeicher.
Wichtige Erkenntnisse
- Eine C-Zeichenkette endet bei ihrem ersten
'\0'. - Die Kapazität schließt das abschließende Nullzeichen ein; die Zeichenkettenlänge nicht.
- Ein Zeichenarray ohne abschließendes Nullzeichen kann nicht als C-Zeichenkette übergeben werden.
- Arraykopien von Literalen sind veränderbar; die Literale selbst nicht.
- Ein früher geschriebenes abschließendes Nullzeichen kürzt den Text, ohne die Größe seines Arrays zu ändern.
2.3 Zeichenketten zeichenweise verarbeiten
Das abschließende Nullzeichen einer Zeichenkette gibt einer Schleife ihren Endpunkt vor. Greife auf die Bytes davor zu, um Text zu zählen, umzuwandeln oder zu filtern. Beim Erstellen einer neuen Zeichenkette muss das Programm selbst ein neues abschließendes Nullzeichen schreiben.
Einmal bis zum Ende laufen
for (size_t i = 0; s[i] != '\0'; i++) {
/* Process s[i]. */
}
Bei jedem Durchlauf prüft die Bedingung, ob das aktuelle Byte zum Text gehört. Die Schleife stoppt vor dem abschließenden Nullzeichen. Ein Aufruf von strlen(s) in dieser Bedingung kann bei jedem Durchlauf die ganze Zeichenkette erneut durchsuchen. Prüfe entweder das aktuelle Byte direkt oder berechne size_t len = strlen(s); einmal, wenn die Länge unverändert bleibt.
Verwende für die Umwandlung in Großbuchstaben ein veränderbares Array und <ctype.h>:
for (size_t i = 0; s[i] != '\0'; i++) {
s[i] = (char) toupper((unsigned char) s[i]);
}
Aus „Hello, World 42“ wird „HELLO, WORLD 42“. Die Umwandlung erhält Komma, Leerzeichen und Ziffern. Die Umwandlung in den vorzeichenlosen Typ macht jedes gespeicherte Byte zu einem zulässigen ctype-Argument.
Kategorien und Wortanfänge zählen
Das ursprüngliche „Hello, World 42“ hat 15 Textbytes: 10 Buchstaben, zwei Ziffern, zwei Leerzeichen und ein Komma. Separate Zähler können isalpha, isdigit und isspace verwenden. Entscheide vor dem Zählen, was ein Wort ist. Für eine Folge von Buchstaben beginnt ein neues Wort, wenn ein Buchstabe auf ein anderes Zeichen folgt oder das erste Zeichen ist:
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;
}
Das Beispiel hat zwei Buchstabenfolgen, „Hello“ und „World“. Die Ziffern bilden nach dieser Definition kein Wort. Wird in_word bei jedem Durchlauf aktualisiert, kann ein Satzzeichen eine Folge beenden.
Nur den Text umkehren
Ermittle für eine Zeichenkette aus Einzelbytezeichen len einmal und tausche dann s[i] mit s[len - 1 - i], solange i < len / 2 gilt. Beim Umkehren von „stressed“ werden zuerst der erste und der letzte Buchstabe vertauscht, dann das nächste Paar, bis „desserts“ entsteht. Das Nullbyte bleibt bei Index len. Würde ein Tausch bei s[len] beginnen, gelangte diese null an den Anfang. Das Umkehren von Bytes auf diese Weise eignet sich für ASCII-Text; mehrbyteige Zeichen müssen vor dem Umordnen dekodiert werden.
Ein gefiltertes Ergebnis erstellen
Verwende unterschiedliche Indizes zum Lesen und Schreiben, weil einige Eingabebytes übersprungen werden:
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';
Das Ergebnis ist „abc“. Das Ziel hat genug Platz, weil das Filtern diese Eingabe nicht verlängern kann. Ohne out[j] = '\0'; hat die Ausgabe kein definiertes Zeichenkettenende.
Verschiebe für eine Caesar-Verschlüsselung Großbuchstaben relativ zu 'A' und Kleinbuchstaben relativ zu 'a' und kopiere dann andere Bytes unverändert. Mit dem Schlüssel 13 wird aus „Hello, World!“ der Text „Uryyb, Jbeyq!“. Eine Wiederholung dieser Verschiebung stellt das Original wieder her, weil zwei Verschiebungen zusammen 26 ergeben. Ein Vergleich wie s[i] == 'a' prüft ein einzelnes Zeichen; verwende strcmp, um vollständigen Text zu vergleichen.
Wichtige Erkenntnisse
- Prüfe das aktuelle Byte oder speichere die Länge zwischen, um wiederholte vollständige Durchläufe zu vermeiden.
- Halte beim Filtern Lese- und Schreibindex getrennt.
- Schreibe nach dem Erstellen einer neuen Zeichenkette ein abschließendes Nullzeichen.
- Kehre die Textbytes um, ohne das abschließende Nullzeichen zu verschieben.
- Definiere eine Wortgrenze, bevor du einen Wortzähler schreibst.
- Wende ASCII-Buchstabenverschiebungen getrennt auf Groß- und Kleinbuchstaben an.
2.4 Erklären, wie UTF-8-Text in char-Arrays gespeichert wird
Textlänge kann die Anzahl der Bytes oder die Anzahl der Unicode-Codepunkte bedeuten. Diese Anzahlen unterscheiden sich oft. Ein C-char-Array macht einzelne Bytes zugänglich, sodass seine Indizes und Zeichenkettenfunktionen nicht automatisch vollständigen Zeichen außerhalb von ASCII folgen.
Zeichen, Codepunkte und Bytes unterscheiden
Eine Zeichenkodierung beschreibt, wie Text gespeichert wird. ASCII verwendet die Codes 0 bis 127 für grundlegende Buchstaben, Ziffern, Satz- und Steuerzeichen. Unicode weist Zeichen aus vielen Schriftsystemen Codepunkte zu: „é“ hat den Codepunkt U+00E9 und „€“ den Codepunkt U+20AC. UTF-8 kodiert Unicode-Skalarwerte mit einem bis vier Bytes.
ASCII-Codepunkte behalten ihre ein Byte langen Kodierungen. Das vorkombinierte „é“ benötigt zwei Bytes, 0xC3 und 0xA9; „€“ benötigt drei. Viele einzelne Emoji-Codepunkte benötigen vier Bytes. Ein angezeigtes Symbol kann mehrere Codepunkte enthalten, sodass selbst die Anzahl der Codepunkte nicht immer der Anzahl sichtbarer Symbole entspricht.
Eine UTF-8-Zeichenkette messen
char s[] = "café";
printf("%zu %zu\n", strlen(s), sizeof s);
Wenn der Ausführungstext in UTF-8 vorliegt, gibt dies „5 6“ aus. Die ersten drei Buchstaben benötigen drei Bytes und „é“ zwei, also insgesamt fünf Textbytes. Das abschließende Nullzeichen fügt das sechste Speicherbyte hinzu. s[3] ist das erste Byte von „é“, nicht der ganze Buchstabe.
Ein Schnitt nach vier Bytes würde 0xC3 erhalten, aber sein Fortsetzungsbyte 0xA9 entfernen. Das Ergebnis wäre kein gültiges UTF-8 mehr. Eine in Bytes angegebene Puffergrenze erfordert daher eine Prüfung der Zeichengrenze, wenn das Programm UTF-8-Text abschneidet.
Anfangsbytes in gültigem UTF-8 zählen
Ein Fortsetzungsbyte beginnt mit den Bits 10. Sein Wert liegt zwischen 0x80 und 0xBF. Ein führendes Byte oder ASCII-Byte hat dieses Muster nicht:
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++;
}
Die Maske 0xC0 behält nur die beiden höchstwertigen Bits; 0x80 stellt das Muster 10 an diesen Positionen dar. Der Test zählt „café“ als vier Codepunkte, indem er seine vier Anfangsbytes zählt. Er setzt gültiges UTF-8 voraus und prüft weder fehlerhafte Sequenzen noch fasst er Codepunkte zu sichtbaren Symbolen zusammen.
Operationen wählen, die die Kodierung erhalten
Das Kopieren einer vollständigen UTF-8-Zeichenkette erhält ihre Bytes. Auch das Verketten vollständiger Zeichenketten, der Vergleich ihrer Bytefolgen auf Gleichheit und die Suche nach einem ASCII-Zeichen oder einer vollständigen UTF-8-Teilzeichenkette funktionieren. UTF-8-Fortsetzungsbytes können nicht mit ASCII-Zeichen oder führenden Bytes verwechselt werden.
strcmp sortiert vorzeichenlose Bytewerte, daher unterscheidet sich seine Reihenfolge von der sprachspezifischen alphabetischen Reihenfolge. In UTF-8 haben gewöhnliche lateinische Buchstaben mit Akzent führende Bytes oberhalb von ASCII-'z'. Ein byteweiser Vergleich ordnet diese Buchstaben daher unabhängig von Wörterbuchregeln nach 'z' ein.
In der Standard-Locale „C“ verarbeiten toupper und isalpha jeweils ein Byte und dekodieren „é“ nicht als Buchstaben. Die explizite Umwandlung in unsigned char macht den Aufruf zulässig, kann ihn aber nicht zu einer Unicode-Umwandlung der Groß- und Kleinschreibung machen. Dafür werden Wide-Character-Funktionen oder eine Unicode-Bibliothek benötigt. Eine Caesar-Schleife nur für ASCII kann Buchstaben mit Akzent erhalten, indem sie nur Bytes von A–Z oder a–z ändert.
Das Nullbyte ist die UTF-8-Kodierung von U+0000; andere kodierte Codepunkte enthalten kein Nullbyte. Das passt zur Konvention des abschließenden Nullzeichens in C. UTF-16 kann Nullbytes innerhalb gewöhnlichen kodierten Textes enthalten, sodass ein UTF-16-Bytearray nicht als gewöhnliche char-Zeichenkette verarbeitet werden kann.
Wichtige Erkenntnisse
strlenzählt Bytes, auch in UTF-8-Text.- Ein Unicode-Codepunkt kann mehrere Bytes belegen.
- Ein char-Index kann innerhalb eines kodierten Zeichens landen.
- Das Zählen von Bytes, die keine Fortsetzungsbytes sind, zählt nur bei gültigem UTF-8 die Codepunkte.
- Byteweiser Gleichheitsvergleich funktioniert für identisch kodierten Text; die Byte-Reihenfolge ist nicht die Wörterbuchreihenfolge.
- ctype-Aufrufe für einzelne Bytes dekodieren kein UTF-8.
3Die Standardfunktionen für Zeichenketten verwenden
Die Standardbibliothek stellt gängige Textoperationen bereit, aber jede Funktion erwartet eine bestimmte Art von Eingabe und eine bestimmte Bedeutung ihres Größenarguments. Lerne, die Länge von Zeichenketten zu bestimmen, sie zu kopieren, anzuhängen, zu vergleichen und zu durchsuchen, und wähle dann größenbegrenzte Formatierung oder Byteoperationen, wo sie passen. Durch das Prüfen der Kapazität und das Deuten der Rückgabewerte wird aus einem kurzen Funktionsaufruf eine vorhersehbare Operation auf bekanntem Speicher.
3.1 Die Länge von Zeichenketten bestimmen, Zeichenketten kopieren und verketten
Die Funktionen in <string.h> arbeiten mit nullterminiertem Text. Sie können seine Länge bestimmen, ihn kopieren oder weiteren Text anhängen, aber die Kopierfunktionen ermitteln nicht die Kapazität des Ziels. Berechne vor dem Schreiben den benötigten Speicher.
Die Länge ohne das abschließende Nullzeichen bestimmen
strlen(s) gibt einen size_t-Wert zurück, der die Anzahl der Bytes vor dem ersten Nullbyte angibt. Daher ist strlen("hello") gleich 5 und strlen("") gleich null. Gib das Ergebnis mit %zu aus.
char buf[64] = "hi";
printf("%zu %zu\n", strlen(buf), sizeof buf);
Dies gibt „2 64“ aus. Das erste Ergebnis misst den aktuellen Text; das zweite den verfügbaren Arrayspeicher. Ein späteres Kopieren kann das erste ändern, ohne das zweite zu ändern. Beide Aufrufe sind hier nützlich, weil buf ein tatsächliches Array ist und bereits eine gültige Zeichenkette enthält.
Eine vollständige Zeichenkette kopieren
strcpy(dest, src) kopiert den Text und sein abschließendes Nullbyte. Die benötigte Kapazität ist die Länge der Quelle plus eins:
char dest[12];
const char *src = "Maple";
if (strlen(src) < sizeof dest) {
strcpy(dest, src);
}
„Maple“ belegt fünf Textbytes und ein abschließendes Nullzeichen und passt daher hinein. Das strikte < ist entscheidend: Eine Quelle, deren Länge der Kapazität entspricht, lässt keinen Platz für ihr Nullbyte. Schlägt die Prüfung fehl, muss das Programm den Fehler behandeln, bevor es das Ziel als neue Zeichenkette verwendet.
Eine Arrayzuweisung wie dest = src; kann den Inhalt nicht ersetzen. Die Zuweisung einer char *-Variablen an eine andere ist zulässig, kopiert aber nur einen Verweis auf denselben Speicher. Sie erstellt keine weitere Kopie der Zeichen.
An eine vorhandene Zeichenkette anhängen
strcat(dest, src) sucht zuerst das bereits in dest vorhandene abschließende Nullzeichen. Es ersetzt dieses durch das erste Byte der Quelle und schreibt ein neues abschließendes Nullzeichen hinter den angehängten Text. Die Zielkapazität muss beide aktuellen Längen plus eins abdecken.
char full[16];
strcpy(full, "Ada");
strcat(full, " ");
strcat(full, "Lovelace");
Die Längen wachsen von 3 über 4 auf 12. Das fertige „Ada Lovelace“ benötigt 13 Bytes Speicher, sodass drei Bytes ungenutzt bleiben. Ein Ziel mit 12 Bytes wäre um ein Byte zu klein, obwohl es der sichtbaren Textlänge entspricht.
Hänge nie an einen nicht initialisierten lokalen Puffer an. Bei char out[20]; strcat(out, "hi"); kann strcat kein festgelegtes Textende finden. Beginne mit char out[20] = "";, schreibe out[0] = '\0'; oder führe als erste Operation eine geprüfte Kopie aus.
Speicherbereiche und Aufwand beim Durchsuchen beachten
Quelle und Ziel dürfen sich bei strcpy oder strcat nicht überlappen. Das Verschieben von Text innerhalb desselben Puffers kann eine Überlappung erzeugen, selbst wenn beide Stellen für sich genommen zulässig sind. memmove ist die für überlappende Bytebereiche vorgesehene Funktion.
strlen muss bis zum abschließenden Nullzeichen suchen. strcat muss das aktuelle Ziel durchsuchen, bevor es anhängt. Wiederholtes Anhängen in einer Schleife durchsucht daher bereits erstellten Text erneut. Wenn du viele Teile zusammensetzt, halte die aktuelle Ausgabelänge fest und schreibe jeden Teil an diesem Index. Prüfe dabei die verbleibende Kapazität und erhalte das abschließende Nullzeichen. Eine bekannte Länge ist nur nützlich, solange sie dem tatsächlichen Inhalt entspricht.
Wichtige Erkenntnisse
strlenmisst den aktuellen Text;sizeofmisst bei einem Array dessen Kapazität.- Eine Kopie benötigt die Länge der Quelle plus ein Byte.
- Zum Anhängen werden beide Zeichenkettenlängen plus ein Byte benötigt.
- Initialisiere das Ziel als Zeichenkette, bevor du etwas anhängst.
- Kopieren oder Anhängen mit überlappenden Zeichenkettenbereichen ist unzulässig.
3.2 Zeichenketten vergleichen und durchsuchen
Der Vergleich von Zeichenketten beantwortet Fragen zum Text, während die Suche eine Stelle innerhalb dieses Textes findet. Keine der beiden Aufgaben wird durch den Vergleich von Arraynamen mit == erledigt. Verwende das Ergebnis der Bibliotheksfunktion entsprechend seiner Bedeutung: als Vergleichsvorzeichen, Länge oder Fundstelle.
Text und Präfixe vergleichen
strcmp(a, b) vergleicht einander entsprechende Bytes als unsigned char-Werte bis zu einem Unterschied oder dem Zeichenkettenende. Das Ergebnis ist null bei gleichem Text, negativ, wenn a zuerst kommt, und positiv, wenn es später kommt. Nur das Vorzeichen ist festgelegt; erwarte nicht genau -1 oder 1.
if (strcmp(command, "quit") == 0) {
puts("Goodbye");
}
Der ausdrückliche Vergleich mit null bedeutet Gleichheit. Ein einfaches if (strcmp(command, "quit")) führt den Zweig bei unterschiedlichem Text aus, weil ein Wert ungleich null wahr ist. command == "quit" würde Speicheradressen vergleichen.
In der ASCII-Reihenfolge kommt „apple“ beim ersten Buchstaben vor „banana“. „Zebra“ kommt vor „apple“, weil der Großbuchstabe 'Z' den Code 90 und der Kleinbuchstabe 'a' den Code 97 hat. „app“ kommt vor „apple“, weil sein Nullbyte vor dem 'l' der anderen Zeichenkette erreicht wird. Dies ist eine Reihenfolge nach Bytes, kein Wörterbuchvergleich.
strncmp(a, b, n) vergleicht höchstens n Bytes. strncmp(s, "--", 2) == 0 erkennt ein Präfix aus zwei Bindestrichen, bedeutet aber nicht, dass die gesamte Zeichenkette nur aus „--“ besteht. Standard-C hat keine Funktion zum Vergleich ganzer Zeichenketten ohne Beachtung der Groß- und Kleinschreibung. Ein Programm kann konvertierte Bytes selbst vergleichen; POSIX stellt strcasecmp in <strings.h> bereit.
Eine Stelle suchen
Verwende strchr für das erste Vorkommen eines Zeichens, strrchr für das letzte und strstr für die erste vollständige Teilzeichenkette. Die Funktionen geben einen Zeiger auf die Fundstelle zurück oder NULL, wenn es keinen Treffer gibt.
char name[] = "report.final.txt";
char *extension = strrchr(name, '.');
if (extension != NULL) {
printf("%s\n", extension);
}
Dies gibt „.txt“ einschließlich des gefundenen Punkts aus. Ein Start am ersten Punkt mit strchr würde „.final.txt“ ausgeben. strstr(name, "final") findet den Rest der Zeichenkette ab „final.txt“; eine Suche nach „draft“ ergibt NULL. Prüfe auf NULL, bevor du ein Suchergebnis ausgibst oder liest.
Ein Trennzeichen finden oder Text zerlegen
strcspn(s, set) zählt die anfänglichen Bytes, die zu keinem der Zeichen in set gehören. Bei "oak:pine,birch" und der Zeichenmenge ":," stoppt die Funktion am Doppelpunkt. Bei einer gültigen Eingabezeichenkette entfernt dieses Muster den ersten Zeilenumbruch:
buf[strcspn(buf, "\n")] = '\0';
Gibt es keinen Zeilenumbruch, ist der zurückgegebene Index die Länge der Zeichenkette, sodass die Zuweisung das bereits vorhandene abschließende Nullzeichen überschreibt.
strtok zerlegt eine veränderbare Zeichenkette anhand beliebiger Zeichen aus einer Menge von Trennzeichen:
char colors[] = "red, green,blue";
char *token = strtok(colors, " ,");
while (token != NULL) {
puts(token);
token = strtok(NULL, " ,");
}
Die Token sind „red“, „green“ und „blue“. Der erste Aufruf übergibt das Array; spätere Aufrufe übergeben NULL, um fortzufahren. Die Funktion schreibt Nullbytes in die Eingabe und speichert zwischen den Aufrufen einen versteckten Zustand. Verwende beschreibbaren Speicher und erwarte nicht, dass die ursprüngliche Zeichenkette mit ihren Trennzeichen erhalten bleibt. Aufeinanderfolgende Trennzeichen erzeugen keine leeren Token.
Wichtige Erkenntnisse
- Verwende
strcmp(a, b) == 0für gleichen Text. - Deute ein Vergleichsergebnis anhand seines Vorzeichens, nicht seines genauen Betrags.
- Ein übereinstimmendes Präfix belegt nicht, dass die ganzen Zeichenketten gleich sind.
- Suchergebnisse müssen auf einen Nullzeiger geprüft werden.
strcspngibt die Länge eines Präfixes zurück;strtokverändert seine Eingabe und merkt sich seine Position.- Die Reihenfolge von Zeichenketten folgt den vorzeichenlosen Bytecodes.
3.3 Beim Kopieren Speichergrenzen einhalten und mit rohem Speicher arbeiten
Eine Längenbegrenzung ist nur nützlich, wenn sie die richtige Größe beschreibt. Manche Funktionen begrenzen die Quellbytes, andere die Zielkapazität; Speicherfunktionen verarbeiten rohe Bytes ohne Zeichenkettenenden. Lies die Vorgaben jeder Funktion, bevor du ihre Schranke festlegst.
Wissen, was n bedeutet
strncpy(dest, src, n) schreibt genau n Positionen, wenn die Quelle kürzer ist, und füllt mit Nullbytes auf. Hat die Quelle mindestens n Textbytes, kopiert die Funktion diese Bytes ohne abschließendes Nullzeichen. Für ein Array mit einer Kapazität größer als null ergibt sich so eine abgeschlossene, gegebenenfalls abgeschnittene Kopie:
strncpy(dest, src, sizeof dest - 1);
dest[sizeof dest - 1] = '\0';
Die letzte Zuweisung ist unerlässlich, wenn die Quelle die Grenze ausschöpft. Das Abschneiden eines Namens oder Befehls kann dennoch seine Bedeutung ändern. Weise daher zu lange Eingaben zurück, wenn eine vollständige Kopie erforderlich ist.
strncat(dest, src, n) hängt höchstens n Textbytes und dann ein abschließendes Nullzeichen an. Die Funktion erhält nicht die Gesamtkapazität. Für eine gültige Zeichenkette, die bereits in einem Zielarray steht, ist der verbleibende Platz für Text sizeof dest - strlen(dest) - 1. sizeof dest als n zu übergeben, kann zu einem Überlauf führen.
Formatieren und Abschneiden erkennen
snprintf, deklariert in <stdio.h>, erhält die gesamte Zielkapazität. Ist die Kapazität größer als null, schreibt die Funktion höchstens ein Textbyte weniger und danach ein abschließendes Nullzeichen:
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");
}
Das vollständige Ergebnis „item-2026“ hat neun Textbytes. Der Puffer speichert „item-20“ und dessen Nullbyte, während needed gleich 9 ist. Der Rückgabewert zählt den Text, der erzeugt worden wäre, ohne das abschließende Nullzeichen. Prüfe ein negatives Ergebnis, bevor du es in size_t umwandelst.
Bytes kopieren und füllen
memcpy(dest, src, n) kopiert genau n Bytes, einschließlich möglicher Nullbytes, ohne nach einem abschließenden Nullzeichen zu suchen. Bei zwei gleich großen Ganzzahlarrays kopiert memcpy(b, a, sizeof a) deren gesamten Speicherinhalt, wenn die Bereiche nicht überlappen. Belegt jede von fünf Ganzzahlen vier Bytes, beträgt die Anzahl 20 Bytes. memmove kopiert Bytes mit definiertem Verhalten bei überlappenden Bereichen.
memset(a, 0, sizeof a) füllt jedes Byte mit null und setzt ein Ganzzahlarray auf null. Das zweite Argument beschreibt jedes Byte, nicht jedes Element. Bei Ganzzahlen mit vier Bytes setzt das Füllen mit 1 die Bytes auf 01 01 01 01, was bei der üblichen Darstellung 16843009 ergibt, nicht die Ganzzahl 1. Verwende eine Schleife, um Ganzzahlwerte ungleich null zuzuweisen.
memcmp(a, b, n) vergleicht gespeicherte Bytes. Null bedeutet, dass diese Bytefolgen übereinstimmen. Vergleiche für die Gleichheit der Werte die Elemente: Die Gleitkommawerte 0.0 und -0.0 werden als gleich verglichen, obwohl sich ihre Darstellungen unterscheiden können.
Andere Bibliotheksfamilien erkennen
strlcpy und strlcat aus BSD wurden 2024 in POSIX aufgenommen, sind aber keine Kernfunktionen von C17. Sie geben eine Länge zurück, die sie zu erzeugen versucht haben; ein Ergebnis mindestens so groß wie die angegebene Größe zeigt ein Abschneiden an. strlcpy schreibt ein abschließendes Nullzeichen, wenn die Größe positiv ist. strlcat tut dies nur, wenn der vorhandene Text innerhalb dieser Größe endet. Die Funktionen sind ab glibc 2.38 verfügbar. Optionale Funktionen aus Anhang K wie strcpy_s fehlen in vielen Bibliotheken, darunter glibc.
Wichtige Erkenntnisse
strncpykann das abschließende Nullzeichen weglassen;strncatbenötigt Platz für ein neues.snprintfgibt die benötigte Länge des vollständigen Textes oder bei einem Fehler einen negativen Wert zurück.- Ein nicht negatives Formatierungsergebnis mindestens so groß wie die Kapazität bedeutet, dass Text abgeschnitten wurde.
- Speicherfunktionen zählen Bytes und stoppen nicht bei null.
- Verwende
memmovefür überlappende Bereiche. memsetfüllt Bytes;memcmpvergleicht Darstellungen.
4Daten in mehrdimensionalen Arrays organisieren
Tabellen ordnen Werte nach mehr als einer Position. Eine C-Tabelle speichert gleich breite Zeilen nacheinander, und verschachtelte Schleifen ermöglichen einem Programm, zeilen- oder spaltenweise zu arbeiten. Lerne, wie du diese Form deklarierst, an eine Funktion übergibst und beide Indizes im zulässigen Bereich hältst. Bei Textlisten stellt sich eine nützliche Frage: Beschreibbare Zeichenzeilen reservieren festen Platz, während Zeigerarrays auf Zeichenketten verweisen, die anderswo gespeichert sind.
4.1 Zweidimensionale Arrays deklarieren und durchlaufen
Eine Tabelle benötigt einen Zeilen- und einen Spaltenindex. In C ist ein zweidimensionales Array ein Array, dessen Elemente selbst Arrays sind. Jede Zeile hat dieselbe Anzahl an Elementen, und jeder Index hat seine eigene Schranke.
Zeilen und Spalten deklarieren
int grid[3][4];
int m[2][3] = {{1, 2, 3}, {4, 5, 6}};
grid enthält drei Zeilen mit jeweils vier Ganzzahlen: insgesamt zwölf Ganzzahlen. Seine Zeilenindizes reichen von 0 bis 2, seine Spaltenindizes von 0 bis 3. grid[1][2] greift auf den dritten Wert in der zweiten Zeile zu.
Verschachtelte geschweifte Initialisierungsklammern zeigen die Einteilung in Zeilen. Fehlende Initialisierungswerte werden null. Du kannst den Compiler in int m[][3] = {{1, 2, 3}, {4, 5, 6}}; die Zeilen zählen lassen, musst aber die Spaltenanzahl angeben. Ohne sie könnte der Compiler nicht bestimmen, wo jede Zeile endet.
Die zeilenweise Speicherung nachvollziehen
C speichert eine vollständige Zeile, bevor die nächste beginnt. In grid bilden die ersten vier gespeicherten Ganzzahlen Zeile 0, die nächsten vier Zeile 1 und die letzten vier Zeile 2. grid[1][2] ist vom Anfang aus die siebte Ganzzahl, an der bei null beginnenden Elementposition 6: eine vollständige Zeile mit vier Elementen, dann zwei weitere Elemente.
Wenn Ganzzahlen vier Bytes belegen, benötigt eine Zeile sechzehn Bytes und die gesamte Tabelle achtundvierzig. An der Deklarationsstelle:
size_t rows = sizeof grid / sizeof grid[0];
size_t cols = sizeof grid[0] / sizeof grid[0][0];
Die erste Division ergibt 3 und die zweite 4. Eine Zeile ist selbst ein Array, weshalb sizeof grid[0] mehr als eine Ganzzahl misst.
Die Tabelle durchlaufen und summieren
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;
}
Die erste Zeile ergibt 11 und die zweite 8. Die Summe dieser Zeilensummen ist 19. Das Zurücksetzen von row_sum innerhalb der äußeren Schleife beginnt für jede Zeile eine neue Summe. Würde die Variable nur einmal vor beiden Schleifen initialisiert, enthielte der zweite ausgegebene Wert auch die erste Zeile.
Setze für Spaltensummen die Spaltenschleife nach außen und die Zeilenschleife nach innen und greife weiterhin auf sales[r][c] zu. Die drei Summen sind 9, 2 und 8 und ergeben zusammen wieder 19. Die Änderung der Durchlaufreihenfolge erfordert keine Änderung der Bedeutung der beiden Indizes. Ein zeilenweiser Durchlauf folgt der zusammenhängenden Speicherung und verbessert bei großen Tabellen oft die Cache-Nutzung.
Jede Dimension innerhalb ihrer Grenzen halten
grid[c][r] vertauscht die Bedeutung der Indizes. Erreicht c den Wert 3, wird eine vierte Zeile angefordert, die nicht existiert. Die Tatsache, dass das gesamte Objekt zwölf Ganzzahlen enthält, erlaubt es nicht, mit einem Spaltenindex die Grenze einer Zeile zu überschreiten.
Schreibe zwei getrennte Indexzugriffe: grid[1][2]. In C17 wertet grid[1, 2] den Kommaausdruck aus und wählt grid[2], eine Zeile, statt eines einzelnen Elements. Bei mehr Dimensionen wird dieselbe Idee wiederholt angewendet: int cube[2][3][4] hat 24 Ganzzahlelemente, die über drei Indizes ausgewählt werden.
Wichtige Erkenntnisse
- Ein zweidimensionales Array ist ein Array gleich breiter Zeilen.
- Bei der zeilenweisen Speicherung werden Zeilen nacheinander gespeichert.
- Halte die Grenzen für Zeilen und Spalten getrennt.
- Setze einen Zeilen- oder Spaltenakkumulator beim Beginn einer neuen Gruppe zurück.
- Nur die erste Dimension darf aus einem Initialisierer abgeleitet werden.
4.2 Tabellen an Funktionen übergeben und Zeichenkettenlisten speichern
Eine Funktion, die eine Tabelle erhält, benötigt genügend Informationen, um jede Zeile zu bestimmen. Bei einer Zeichenkettenliste kommt eine weitere Entscheidung hinzu: Speichere die Zeichen in gleich breiten Zeilen oder speichere Verweise auf Zeichenketten unterschiedlicher Länge.
Die Zeilenbreite eines Tabellenparameters angeben
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;
}
}
}
Die erste Dimension kann entfallen, weil die Zeilenanzahl separat übergeben wird. Die Breite 4 definiert den Typ jeder Zeile. Ohne sie wäre der Abstand zwischen den Zeilen für g[r][c] nicht bekannt. Eine Deklaration mit int g[][] ist ungültig. Änderungen über diesen Parameter bleiben in der Tabelle des Aufrufers erhalten; ein Tabellenparameter, der nur gelesen wird, kann const int g[][4] verwenden.
Seit C99 akzeptieren Compiler, die Parameter mit variabel modifiziertem Typ unterstützen, Dimensionen, die zuvor in der Parameterliste deklariert wurden:
void print_grid(int rows, int cols, int g[rows][cols]);
GCC und Clang unterstützen diese C17-Form. Sie ermöglicht einer Funktion, verschiedene Zeilenbreiten entgegenzunehmen; übergib positive Dimensionen, die zur tatsächlichen Tabelle passen. C23 verlangt Unterstützung für diese Parametertypen, während automatische Arrayobjekte variabler Länge ein separates optionales Merkmal bleiben. Parameter mit fester Breite sind nützlich, wenn die Compilerunterstützung begrenzt ist.
Zeichenketten in Zeilen speichern
char days[7][10] = {
"Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday"
};
Jede Zeile hat unabhängig von ihrer aktuellen Textlänge Speicher für zehn Zeichen. „Wednesday“ benötigt alle zehn Positionen: neun Buchstaben und ein abschließendes Nullzeichen. days[2] wählt diese ganze Zeile als Zeichenkette; days[2][0] wählt 'W'.
Eine Zeile kann mit %s ausgegeben, mit strlen gemessen oder mit strcmp verglichen werden. Sie kann auch eine geprüfte Kopie aufnehmen. strcpy(days[0], "Mon"); passt in zehn Bytes und ändert den Text von Zeile 0. Die Zeile bleibt zehn Bytes breit und die nächste Zeile bleibt an derselben Stelle. Ein Ersatztext mit zehn Textbytes würde elf Bytes benötigen und nicht hineinpassen.
Verweise auf Literale speichern
const char *names[] = {"Ann", "Bartholomew", "Li"};
Dies deklariert ein Zeigerarray. Jedes Element verweist auf ein Literal mit seiner eigenen Länge plus einem abschließenden Nullzeichen. names[1] verweist auf „Bartholomew“ und names[1][0] ist 'B'. Die Literale dürfen nicht verändert werden.
Die Zeigerelemente können sich ändern, obwohl die Zeichen der Literale nicht verändert werden dürfen. names[0] = "Anna"; ändert, auf welches Literal das erste Element verweist; es verlängert „Ann“ nicht an Ort und Stelle. In der Form mit 2D-Zeilen ist ein Element ein Zeichenarray mit fester Breite und kann nicht auf diese Weise neu zugewiesen werden. Wähle beschreibbare Zeilen für Text, der direkt bearbeitet wird, und Verweise auf Literale für eine Liste fester Textoptionen. Der Kommandozeilenparameter argv hat die Form eines Zeigerarrays, obwohl die Zeichen der übergebenen Argumente veränderbar sind.
Wichtige Erkenntnisse
- Tabellenparameter benötigen jede Dimension nach der ersten.
- Eine separat übergebene Dimension muss zur tatsächlichen Tabelle passen.
- Jede Zeichenkettenzeile benötigt innerhalb ihrer festen Breite ein eigenes abschließendes Nullzeichen.
- Ein Zeigerarray speichert Verweise, keine gleich breiten Zeichenzeilen.
5Kommandozeilenargumente verarbeiten
Eine Kommandozeile stellt einem Programm beim Start Text bereit. Mit der Argumentanzahl, der Gruppierung durch die Shell und Zeichenkettenoperationen kann das Programm diesen Text deuten, ohne vorauszusetzen, dass ein Argument existiert oder bereits eine Zahl enthält. Verfolge einen geprüften Weg von argv zu einem nutzbaren Wert: Prüfe, ob das Argument vorhanden ist, konvertiere es vollständig, erkenne Konvertierungsfehler und setze den Wertebereich durch, den das Programm tatsächlich akzeptiert.
5.1 argc und argv auslesen
Kommandozeilenargumente ermöglichen es dem Aufrufer, beim Start eines Programms Text bereitzustellen. Die Laufzeitumgebung stellt diese Zeichenketten main zur Verfügung. Prüfe ihre Anzahl, bevor du über Indizes darauf zugreifst.
Die Argumentliste lesen
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
printf("%d: %s\n", i, argv[i]);
}
return 0;
}
Binde <stdio.h> für die Ausgabe ein. argc ist die Argumentanzahl und argv die Liste der Zeichenketten. Die gleichwertige Parameterschreibweise char **argv beschreibt dieselbe Schnittstelle. Die Schleife beginnt bei 1, weil Index 0 normalerweise den Namen enthält, mit dem das Programm gestartet wurde.
In einer gewöhnlichen Shell liefert der Aufruf ./greet Ada Lovelace drei Zeichenketten: „./greet“, „Ada“ und „Lovelace“. Somit ist argc gleich 3 und das letzte Benutzerargument ist argv[2]. Bei argv[argc], hier argv[3], steht ein Nullzeiger; er ist ein Wächterwert nach den eigentlichen Argumentzeichenketten.
Text durch die Shell gruppieren lassen
Anführungszeichen beeinflussen, wie die Shell Argumente bildet. Der Aufruf ./greet "Ada Lovelace" liefert ein Benutzerargument, das ein Leerzeichen enthält. Die Anführungszeichen gruppieren die Wörter und werden nicht Teil des Argumenttextes. argc ist jetzt 2.
Unix-artige Shells können auch Muster erweitern, bevor das Programm startet. Bei ./count *.txt erhält das Programm üblicherweise die von der Shell gefundenen Namen als separate Argumente. Es erhält die resultierenden Zeichenketten, keinen Auftrag, das Muster selbst zu erweitern. Das genaue Verhalten bei der Erweiterung hängt von der Shell und ihren Einstellungen ab.
Ein fehlendes oder zusätzliches Argument zurückweisen
Wenn ein Programm genau ein Benutzerargument benötigt, verlange argc == 2, bevor du es verwendest:
if (argc != 2) {
printf("Usage: %s name\n", argv[0]);
return 1;
}
printf("Hello, %s\n", argv[1]);
Wenn argc gleich 1 ist, ist argv[1] der Nullzeiger als Wächterwert und kann weder an strlen übergeben noch mit %s ausgegeben werden. Indizes hinter dem Wächterwert liegen außerhalb des Argumentarrays.
Die Rückgabe von null aus main signalisiert Erfolg. Ein Status ungleich null, wie die 1 oben, teilt der Shell mit, dass das Programm nicht erfolgreich abgeschlossen wurde. <stdlib.h> stellt auch EXIT_SUCCESS und EXIT_FAILURE bereit.
Jedes Argument als Zeichenkette behandeln
argv[i][0] greift auf das erste Zeichen des Arguments zu; strlen(argv[i]) misst seine Textbytes; strcmp(argv[i], "-v") == 0 prüft eine vollständige Option. Ein Argument, das wie eine Zahl aussieht, ist weiterhin Text und muss vor einer Berechnung konvertiert werden.
argv[0] hält normalerweise fest, wie das Programm gestartet wurde, kann aber leer sein, wenn diese Information nicht verfügbar ist. Es ist für Meldungen nützlich, nicht für eine feste Identitätsprüfung. Die Zeichen der Argumente dürfen während des Programms verändert werden, aber der bereitgestellte Speicher lässt nicht darauf schließen, dass ein längerer Ersatz hineinpasst. Um eine Erweiterung anzuhängen oder Text anderweitig zu verlängern, kopiere ihn zuerst in einen separat bemessenen Puffer.
Wichtige Erkenntnisse
argczählt die übergebenen Argumentzeichenketten einschließlich des üblichen Eintrags für den Startnamen.- Prüfe die Argumentanzahl, bevor du ein Benutzerargument liest.
argv[argc]ist ein Nullzeiger, keine leere Argumentzeichenkette.- Anführungszeichen in der Shell können Leerzeichen innerhalb eines Arguments erhalten.
- Verwende Zeichenkettenoperationen für Argumenttext und Konvertierung für Zahlen.
5.2 Numerische Argumente konvertieren und auf Zulässigkeit prüfen
Ein numerisches Argument kommt als Zeichenfolge an, nicht als Ganzzahl. Eine zuverlässige Konvertierung prüft drei getrennte Fragen: Haben Ziffern eine Zahl gebildet, hat die Konvertierung den erforderlichen Text verwendet und ist der resultierende Wert zulässig?
Den Verlust von Fehlerinformationen vermeiden
atoi, deklariert in <stdlib.h>, führt eine einfache Konvertierung durch. Es wandelt „13“ in 13 um, aber auch „12abc“ in 12. Sowohl für „0“ als auch für Text wie „abc“, der nicht mit einer Zahl beginnt, gibt die Funktion null zurück. Ein Ergebnis außerhalb des Wertebereichs führt zu undefiniertem Verhalten. Daher ist ein Test auf ausschließlich Ziffern mit anschließendem atoi bei sehr langen Eingaben weiterhin unsicher.
Eine Formatregel wie „nur eine oder mehrere Ziffern“ erfordert eine separate Prüfung. Weise eine leere Zeichenkette zurück und verlange, dass jedes Byte isdigit((unsigned char) c) erfüllt. Diese Regel akzeptiert „13“ und weist „13a“, „-5“ und „“ zurück. Sie prüft die Schreibweise, nicht ob der Wert in eine Maschinenganzzahl passt.
strtol verwenden und die Endposition untersuchen
#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;
}
Dieses Fragment folgt auf eine Prüfung der Argumentanzahl und verwendet <stdio.h> für puts. Die 10 wählt die Dezimalkonvertierung. end erhält die Stelle, an der die Konvertierung endete; &end ermöglicht der Funktion, diese Stelle zu speichern. *end liest das dortige Zeichen. Zeigerarithmetik ist nicht nötig.
Für „13a“ ist der Rückgabewert 13, aber *end ist 'a', sodass der Text zurückgewiesen wird. Bei einer leeren Zeichenkette oder „abc“ entspricht end dem Anfang des Arguments und zeigt damit, dass keine Zahl eingelesen wurde. Beide Prüfungen sind nötig: Würde nur das abschließende Nullzeichen geprüft, würde eine leere Zeichenkette fälschlich akzeptiert.
Setze errno vor jeder Konvertierung auf null zurück. Ein Wert außerhalb des long-Bereichs veranlasst strtol, einen Endwert zurückzugeben und ERANGE zu setzen. Ohne das Zurücksetzen könnte ein alter Fehler dieser Konvertierung zugeschrieben werden. Prüfe das Ergebnis, bevor du es in den engeren Typ int umwandelst; verwende INT_MIN und INT_MAX aus <limits.h>, wenn der gesamte int-Bereich zulässig ist.
Die eigene Regel des Programms anwenden
strtol akzeptiert führende Leerraumzeichen und ein optionales Vorzeichen. Entscheide, ob diese Formen zulässig sind. Verlangt die Schnittstelle ausschließlich Ziffern, prüfe zusätzlich zu den Konvertierungsprüfungen, dass die Eingabe nicht leer ist und nur Ziffern enthält. Prüfe nach einer erfolgreichen Konvertierung den anwendungsspezifischen Wertebereich, bevor du einen Wert als Anzahl oder Index verwendest. „0“ kann ein vollkommen gültiger numerischer Text und dennoch für eine Anzahl unzulässig sein, die positiv sein muss.
Ein Caesar-Programm, das einen nicht negativen Schlüssel akzeptiert, prüft zuerst argc, dann, ob der Schlüssel ausschließlich aus Ziffern besteht, und anschließend das vollständige strtol-Ergebnis und den Wertebereich. Die Reduktion eines zulässigen Schlüssels mit % 26 ergibt vor der Buchstabenarithmetik eine Verschiebung von 0 bis 25. Schlüssel 27 hat die Wirkung von Schlüssel 1. Führe die Wertebereichsprüfung vor einer expliziten Umwandlung in einen engeren Typ oder einer möglicherweise überlaufenden Berechnung durch. Für Dezimalwerte bietet strtod die entsprechende Schnittstelle für Endposition und Wertebereichsfehler.
Wichtige Erkenntnisse
- Argumente, die wie Zahlen aussehen, müssen dennoch konvertiert werden.
atoikann eine gültige null nicht von einem Konvertierungsfehler unterscheiden.- Prüfe, ob
strtoletwas gelesen und den gesamten erforderlichen Text verarbeitet hat. - Setze
errnozurück, prüfeERANGEund prüfe dann den anwendungsspezifischen Wertebereich. - Eine Prüfung auf ausschließlich Ziffern schließt einen Überlauf nicht aus.
- Prüfe einen Wert auf Zulässigkeit, bevor du ihn in einen engeren Typ umwandelst oder als Index verwendest.
6Zugriffe außerhalb der Speichergrenzen und Pufferüberläufe verhindern
Fehler an Speichergrenzen verbinden mehrere frühere Ideen: Eine Schleife kann einen zusätzlichen Durchlauf ausführen, Text kann ein zusätzliches Byte benötigen, und eine Funktion kann die Kapazität eines Arrays aus dem Blick verlieren. Lerne, den unzulässigen Zugriff zu erkennen, bevor du seine Folgen vorhersagst. Lies und kopiere dann Eingaben innerhalb bekannter Grenzen und prüfe Erfolg und Vollständigkeit getrennt, damit ein ausreichend großer Puffer auch die Daten enthält, die das Programm annehmen sollte.
6.1 Zugriffe außerhalb der Speichergrenzen und ihre Folgen erkennen
Jeder Arrayzugriff benötigt einen zulässigen Index. C schreibt zur Laufzeit keine Prüfung der Arraygrenzen vor, daher muss der Programmierer den Index innerhalb des Objekts halten. Ein Zugriff außerhalb seines Bereichs hat undefiniertes Verhalten: Die Sprache garantiert kein bestimmtes Ergebnis.
Einen unzulässigen Zugriff erkennen
Für int a[5] sind die Indizes 0 bis 4 zulässig. Index 5 ist bereits zu groß und -1 ist negativ. Ein Compiler kann einen sichtbaren Fehler melden, aber viele unzulässige Indizes hängen von Eingaben zur Laufzeit ab und werden beim Kompilieren nicht zurückgewiesen.
Ein fehlerhaftes Programm kann scheinbar funktionieren, abstürzen oder unerwartete Werte erzeugen. Ein Schreibzugriff kann benachbarte Daten beschädigen; das Symptom kann später bei einer unabhängigen Berechnung auftreten. Ein Lesezugriff kann auf Bytes außerhalb des vorgesehenen Objekts zugreifen. Keine der beiden Operationen hat ein verlässliches Ergebnis. Auch die Wiederholung eines erfolgreichen Laufs kann nicht belegen, dass der Zugriff zulässig ist.
Jeden Schreibzugriff einschließlich des abschließenden Nullzeichens zählen
char name[8];
strcpy(name, "Christopher");
„Christopher“ hat elf Textbytes. Beim Kopieren wird auch das abschließende Nullzeichen geschrieben, sodass dieser Aufruf versucht, zwölf Bytes in acht Positionen zu schreiben. Das ist ein Pufferüberlauf. Auf realen Systemen kann ein Überlauf andere Daten oder Steuerinformationen beschädigen. In der Vergangenheit nutzten Angriffe wie der Morris-Wurm von 1988 solche Fehler bei der Eingabeverarbeitung aus.
Ein Überlauf kann nur ein Byte umfassen:
char s[5];
for (int i = 0; i <= 5; i++) {
s[i] = 'x';
}
Der Schleifenrumpf läuft sechsmal und beschreibt Index 5. Eine Änderung der Bedingung zu i < 5 hält fünf Zeichenschreibzugriffe innerhalb des Arrays, erzeugt aber weiterhin keine Zeichenkette. Schreibe für eine Zeichenkette aus vier 'x'-Zeichen nur die Indizes 0 bis 3 und speichere dann '\0' bei Index 4.
Berechnete Indizes auf Zulässigkeit prüfen
Eine Häufigkeitstabelle kann ein Zeichen in einen Index umwandeln, aber die Subtraktion allein garantiert keinen zulässigen Bereich:
int counts[26] = {0};
if (c >= 'a' && c <= 'z') {
counts[c - 'a']++;
}
Bei ASCII-Reihenfolge begrenzt dieser Test das Ergebnis auf 0 bis 25. Für 'A' oder ein Satzzeichenbyte könnte c - 'a' ohne den Test einen negativen Index erzeugen. Prüfe den ursprünglichen Wert vor dem Indexzugriff; eine Prüfung nach dem Schreiben kommt zu spät.
Prüfe bei einer Tabelle Zeile und Spalte getrennt. In int g[2][3] ist ein Spaltenindex von 3 unzulässig, selbst wenn die nächste Zeile im Speicher angrenzt. Der Speicher der gesamten Tabelle erlaubt es nicht, die Grenze einer Zeile zu überschreiten.
Zur Operation passende Prüfungen verwenden
Eine Arrayschleife verwendet eine strikte obere Schranke; beim Anhängen wird die Anzahl mit der Kapazität verglichen; beim Kopieren einer Zeichenkette wird die Textlänge plus eins geprüft. Funktionen erhalten tatsächliche Längen oder Kapazitäten von ihren Aufrufern. Diese Prüfungen betreffen unterschiedliche Größen, sodass eine fest im Code hinterlegte Größe nicht alle ersetzen kann.
Kompiliere mit Warnungen wie -Wall -Wextra. -fsanitize=address -g von GCC und Clang kann viele unzulässige Zugriffe auf ausgeführten Pfaden melden und die Quelltextstelle anzeigen. Solche Tests helfen, Fehler zu finden, aber bei nicht ausgeführten Pfaden müssen die Grenzen weiterhin durch Nachdenken geprüft werden. Überprüfe jeden indizierten Lese- und Schreibzugriff, indem du sein Objekt, dessen Grenze und die Bedingung bestimmst, die den Index zulässig hält.
Wichtige Erkenntnisse
- Lese- und Schreibzugriffe außerhalb der Speichergrenzen haben undefiniertes Verhalten.
- Ein erfolgreicher Lauf ist kein Beleg dafür, dass ein Index zulässig ist.
- Beim Kopieren einer Zeichenkette zählt das abschließende Nullzeichen zu den geschriebenen Bytes.
- Prüfe Daten auf Zulässigkeit, bevor du daraus einen Arrayindex ableitest.
- Prüfe jede Tabellendimension unabhängig.
- Warnungen und Sanitizer helfen, Programmfehler auf getesteten Pfaden zu erkennen.
6.2 Text unter Einhaltung der Puffergrenzen lesen und kopieren
Die Länge einer Eingabe wird von der Person bestimmt, die sie bereitstellt. Gib Lesefunktionen eine tatsächliche Puffergrenze und prüfe ihre Rückgabewerte. Entscheide dann, ob die Daten eine vollständige Zeile bilden und ob sie zu den eigenen Regeln des Programms passen.
Ein Wort mit einer Feldbreite lesen
Für char word[16]; liest scanf("%15s", word) höchstens fünfzehn Zeichen, die keine Leerraumzeichen sind, und hängt '\0' an. Die Feldbreite lässt eine der sechzehn Positionen für das abschließende Nullzeichen frei. %s ohne Feldbreite hat keine Längenbegrenzung; gets hatte für Zeilen dasselbe grundlegende Problem und wurde in C11 aus C entfernt.
Prüfe, ob scanf 1 zurückgibt, bevor du ein Wort verwendest. Die Funktion liest ein Wort, keine ganze Zeile mit Leerzeichen. Überzählige Bytes des Wortes können ungelesen bleiben. Eine Feldbreite schützt also den Speicher, entscheidet aber nicht, ob die gesamte Eingabe zulässig ist.
Eine Zeile mit fgets lesen
char buf[8];
if (fgets(buf, sizeof buf, stdin) == NULL) {
return 1;
}
fgets liest hier höchstens sieben Zeichen und schreibt ein abschließendes Nullbyte. Die Funktion behält einen Zeilenumbruch, wenn sie ihn vor der Grenze erreicht. Eine Zeile mit „hello“ und einem Zeilenumbruch wird zu "hello\n" plus dem abschließenden Nullzeichen. Auch das Lesen einer letzten Zeile, die am Dateiende ohne Zeilenumbruch endet, kann erfolgreich sein. Wurden vor dem Dateiende keine Zeichen gelesen oder trat ein Lesefehler auf, ist das Ergebnis NULL; verarbeite diesen Puffer nicht als neue Eingabe.
Einen gespeicherten Zeilenumbruch von einer unvollständigen Zeile unterscheiden
Finde einen Zeilenumbruch, bevor du ihn entfernst:
size_t end = strcspn(buf, "\n");
int had_newline = buf[end] == '\n';
buf[end] = '\0';
Binde <string.h> ein. Wurde kein Zeilenumbruch gespeichert, ist end die Textlänge und die Zuweisung überschreibt das vorhandene abschließende Nullzeichen. Ein fehlender Zeilenumbruch kann eine unvollständige Zeile, einen genau vor einem Zeilenumbruch gefüllten Puffer oder eine letzte Zeile bedeuten, die am Dateiende endete. Er allein belegt nicht, dass weitere Zeichen verbleiben.
Bei der Eingabezeile „abcdefghij“ und einem Puffer mit acht Bytes speichert der erste Lesezugriff „abcdefg“ und der nächste kann „hij\n“ lesen. Würde der zweite Abschnitt als eigene Antwort behandelt, würde eine zusammengehörige Eingabe geteilt. Ein Programm kann eine Zeile ohne erforderlichen Zeilenumbruch zurückweisen oder ihren Rest lesen und verwerfen, bevor es erneut fragt. Lege die Eingaberegel fest, bevor du sie implementierst.
Für bis zu dreißig ASCII-Zeichen eines Namens plus einen Zeilenumbruch und ein abschließendes Nullzeichen verwende 32 Bytes. Bei UTF-8 ist dies eine Bytegrenze: Dreißig angezeigte Zeichen können mehr Speicher benötigen.
Erst nach Prüfung aller Anforderungen kopieren
char user[10];
if (argc != 2) return 1;
if (strlen(argv[1]) >= sizeof user) {
puts("Name too long");
return 1;
}
strcpy(user, argv[1]);
Die Prüfung der Argumentanzahl kommt zuerst. Die Längenprüfung lässt dann höchstens neun Textbytes zu, sodass die zehnte Position für null frei bleibt. Eine schreibende Funktion sollte die Kapazität explizit erhalten, etwa als read_name(out, capacity), weil sizeof out innerhalb der Funktion einen Zeigerparameter misst.
Lies bei numerischer Eingabe eine vollständige Zeile und konvertiere sie mit strtol. Prüfe dabei die Endposition, einen Wertebereichsfehler und den zulässigen Wertebereich. Ein fehlgeschlagener Aufruf von scanf("%d", &n) kann den problematischen Text ungelesen zurücklassen; eine blinde Wiederholung kann an denselben Bytes endlos scheitern. Prüfe mindestens die Zuweisungsanzahl und behandle die unzulässige Eingabe, bevor du es erneut versuchst.
Wichtige Erkenntnisse
- Setze die Feldbreite beim Lesen eines Wortes auf eins weniger als die Pufferkapazität.
- Prüfe ein Leseergebnis, bevor du seinen Puffer verwendest.
fgetsbehält einen Zeilenumbruch nur, wenn die Funktion ihn innerhalb der Grenze erreicht.- Ein fehlender Zeilenumbruch belegt für sich allein nicht, dass Eingabe übrig ist.
- Plane Platz für Textbytes, einen gegebenenfalls beibehaltenen Zeilenumbruch und das abschließende Nullzeichen ein.
- Prüfe, ob ein Argument vorhanden ist, bevor du seinen Text misst und kopierst.
- Übergib die Pufferkapazität an eine Funktion, die in den Puffer schreibt.
Arrays und Zeichenketten in C: Beispiele
1Ein Array von der Deklaration bis zum Funktionsaufruf verfolgen
13 Schritte
- 1EinführungMehreren Werten einen Namen geben
Ein Array enthält eine feste Anzahl von Elementen eines Typs. Eine Schleife wählt Positionen aus, während eine Funktion über einen Arrayparameter mit demselben Speicher arbeiten kann.
Verfolge vier Sensormesswerte von ihrer Deklaration über eine Summe bis zu einem Mittelwert. Halte bei jedem Schritt drei Größen auseinander: die Anzahl der Elemente, den letzten zulässigen Index und die Anzahl der belegten Speicherbytes.
- 2BeispielDie Anfangspositionen lesen
int readings[4] = {11, 16, 13};Es gibt vier Elemente mit den Indizes 0 bis 3. Der Initialisierer setzt 11, 16 und 13 an die ersten drei Positionen. Der ausgelassene Wert wird null, sodass das Array
{11, 16, 13, 0}ist.Die null ist eine Initialisierungsregel, kein Zeichen dafür, dass das Array nur drei Elemente hat. Bei einem lokalen Array ohne Initialisierer müssten die Werte vor der Verwendung geschrieben werden.
- 3ErklärungElemente an der Deklaration zählen
size_t n = sizeof readings / sizeof readings[0];Der Zähler misst alle vier Elemente und der Nenner eines. Wenn
intvier Bytes belegt, ist dies sechzehn geteilt durch vier und ergibt 4. Derselbe Ausdruck funktioniert auch bei einer anderen Ganzzahlgröße.readings[3]ist das letzte Element.readings[4]liegt hinter dem Ende, obwohl die Anzahl der Arrayelemente 4 ist.size_tist der vorzeichenlose Typ für dieses Größenergebnis. - 4HerausforderungDen letzten Messwert ändern
Die Deklaration lautet weiterhin
int readings[4] = {11, 16, 13};. Welche Zuweisung ersetzt die null durch 20 und welche Anzahl ergibt sich?---reveal---
Verwende
readings[3] = 20;. Die resultierenden Werte sind{11, 16, 13, 20}und die Anzahl bleibt 4. Eine Elementzuweisung ändert einen Wert, nicht die Anzahl der Positionen. Index 4 wäre eine Position zu weit und kann diesem Array keinen Speicher hinzufügen. - 5BeispielDie Summe Durchlauf für Durchlauf bilden
int sum = 0; for (size_t i = 0; i < n; i++) { sum += readings[i]; }Bei
{11, 16, 13, 20}nimmt die laufende Summe die Werte 11, 27, 40 und 60 an. Nach Index 3 wird i zu 4 und die Bedingung schlägt vor einem weiteren Zugriff fehl.Der Startwert null gibt der Summe einen Ausgangspunkt für den ersten Wert. Durch die strikte obere Schranke greift die Schleife auf genau die vier vorhandenen Elemente zu.
- 6KernaussageDen Nachkommateil eines Mittelwerts erhalten
Für diese vier Messwerte ergibt
(double) sum / nden Wert 15,0. Die explizite Typumwandlung erfolgt vor der Division, sodass die Operation bei einer anderen Summe ein Ergebnis mit Nachkommateil erzeugen kann.Wäre die Summe 61, ergäbe die Ganzzahldivision
sum / 4den Wert 15, währendsum / 4.0den Wert 15,25 ergäbe. Die Zuweisung des bereits abgeschnittenen ganzzahligen Ergebnisses an einen double-Wert ergäbe nur 15,0; der abgeschnittene Nachkommateil würde nicht wiederhergestellt. - 7TippEinen zulässigen Startwert wählen
Eine Maximumsuche benötigt einen Startwert aus einem nicht leeren Array. Initialisiere für
{-6, -2, -9}den Wertmaxmit -6 und ersetze ihn durch -2, wenn dieser größere Wert auftritt. Der letzte Vergleich lässt -2 unverändert.Eine Initialisierung von max mit null würde null zurückgeben, obwohl dieser Wert nicht vorhanden ist. Auch bei einer linearen Suche ist die passende Abbruchregel wichtig: Eine Abweichung belegt keinen Misserfolg; spätere Elemente müssen weiterhin geprüft werden.
- 8HerausforderungEine Suche fortsetzen
Eine Suche nach 13 prüft
{11, 16, 13, 20}. Nach der Prüfung von Index 0 gibt sie -1 zurück, weil 11 von 13 abweicht. Was sollte sich ändern und wo liegt der Treffer?---reveal---
Prüfe nach einer Abweichung weiter und gib -1 erst zurück, wenn alle Kandidaten nicht übereinstimmen. Hier weicht auch Index 1 ab, aber Index 2 stimmt überein. Eine erfolgreiche Suche kann dort stoppen. Der erste fehlgeschlagene Vergleich beschreibt ein Element, nicht das gesamte Array.
- 9VerbindungDen Mittelwert in eine Funktion verlagern
double mean(const int a[], size_t n);Der Arrayparameter ermöglicht den gemeinsamen Zugriff auf die Elemente des Aufrufers. Er enthält nicht deren Anzahl, daher liefert der zweite Parameter sie.
constverhindert bei dieser nur lesenden Operation versehentliche Schreibzugriffe über a.Berechne die Anzahl dort, wo
readingsdeklariert ist, und rufemean(readings, n)auf. Innerhalb von mean misstsizeof aeinen Zeigerparameter statt des Arrays mit vier Elementen des Aufrufers. - 10BeispielDen leeren Bereich vor der Division behandeln
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; }Für die vier kleinen Messwerte bleibt die Summe 60 und das Ergebnis ist 15,0. Für n gleich null ist das gewählte Ergebnis 0,0 und es findet weder ein Arrayzugriff noch eine Division statt. Der Aufrufer muss eine korrekte, verwendbare Anzahl übergeben.
- 11HerausforderungEin verändertes Array des Aufrufers erklären
Eine andere Funktion erhält
int a[], size_t nund setzt jedes besuchtea[i]auf null. Was geschieht mit den Messwerten des Aufrufers und wie könnte eine Funktion, die nur eine Summe berechnet, solche Schreibzugriffe verhindern?---reveal---
Die Elemente des Aufrufers werden null, weil die Funktion in den gemeinsam genutzten Arrayspeicher schreibt. Die vier Positionen existieren weiterhin. Eine Funktion, die nur eine Summe berechnet, sollte
const int a[]verwenden, wodurch Elementzuweisungen über a unzulässig werden. Eine Änderung einer skalaren Kopie von n würde die Zählvariable des Aufrufers nicht verändern. - 12HerausforderungNur die vorhandenen Messwerte verwenden
Ein Puffer hat die Kapazität 10, aber nur vier Messwerte sind belegt. Vor der Berechnung eines Mittelwerts muss ein neuer Messwert angehängt werden. Welche Prüfungen und welche Anzahl sollte das Programm verwenden?
---reveal---
Prüfe, ob die Anzahl belegter Elemente, 4, kleiner als die Kapazität 10 ist. Schreibe bei Index 4 und aktualisiere dann die Anzahl auf 5. Übergib diese Anzahl belegter Elemente an mean, sodass die Funktion genau die fünf Werte verarbeitet. Die Übergabe der Kapazität würde Positionen einschließen, für die keine Messwerte bereitgestellt wurden.
- 13ZusammenfassungJede Anzahl ihrer Aufgabe zuordnen
Eine Deklaration erstellt die Positionen, eine Schleife durchläuft die belegten und eine Funktion erhält die nutzbare Anzahl ausdrücklich. Ein Parameter, über den nur gelesen wird, verhindert, dass Berechnungen die Eingabe überschreiben.
Verfolge bei der Überprüfung von Arraycode den ersten und den letzten besuchten Index sowie den Zustand jedes Akkumulators. Beginne bei rückwärts laufenden
size_t-Schleifen bei n, fahre fort, solange i positiv ist, und greife aufa[i - 1]zu, sodass ein leerer Bereich keine Subtraktion von null erfordert.
2Gespeicherte Bytes für Textoperationen nutzen
16 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungText und Speicherung betrachten
- 2ErklärungEin Zeichen als Code lesen
- 3HerausforderungInnerhalb des Alphabets wieder von vorn beginnen
- 4BeispielEine beschreibbare Zeichenkette erstellen
- 5KernaussageDas Ende verschieben, ohne den Speicher zu verschieben
- 6HerausforderungEine Position für das Ende freilassen
- 7BeispielLesen und Schreiben trennen
- 8BeispielDie neue Zeichenkette abschließen
- 9HerausforderungBuchstaben ohne das abschließende Nullzeichen umkehren
- 10TippBytes mit zulässigen Argumenten klassifizieren
- 11BeispielEin UTF-8-Zeichen mit Akzent darstellen
- 12ErklärungFortsetzungsbytes erkennen
- 13HerausforderungEin weiteres kodiertes Wort messen
- 14VerbindungOperationen wählen, die Zeichen ganz lassen
- 15HerausforderungEine vorgeschlagene Abkürzung für Text prüfen
- 16ZusammenfassungDas Ende und die Zeichengrenzen finden
3Text zusammensetzen, durchsuchen und sicher formatieren
14 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungDie Vorgaben jeder Funktion bestimmen
- 2BeispielMit einer vollständigen Kopie beginnen
- 3BeispielAm vorhandenen Ende anhängen
- 4HerausforderungEntscheiden, ob ein weiterer Anhang hineinpasst
- 5ErklärungGleichen Text anhand von null erkennen
- 6BeispielAb einer Fundstelle lesen
- 7HerausforderungEine präfixbasierte Befehlsprüfung untersuchen
- 8TippBei der Tokenisierung eine Textänderung erwarten
- 9BeispielInnerhalb einer bekannten Kapazität formatieren
- 10HerausforderungDasselbe Format bei weniger Platz deuten
- 11KernaussageEine Schranke kann Quellbytes zählen
- 12BeispielEinen überlappenden Bereich verschieben
- 13HerausforderungNach dem Verschieben ein vollständiges Ergebnis erhalten
- 14ZusammenfassungBytes von Textergebnissen unterscheiden
4Eine Tabelle lesen und eine Speicherform für Zeichenkettenlisten wählen
10 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungJeder Position zwei Indizes geben
- 2BeispielWerte in Zeilen einordnen
- 3BeispielEine Zeilensumme am Zeilenanfang zurücksetzen
- 4HerausforderungDie Gruppierung ändern, die Indizes beibehalten
- 5ErklärungEiner Funktion die Zeilenbreite mitteilen
- 6BeispielPro Zeile eine abgeschlossene Zeichenkette vorsehen
- 7KernaussageVerweise erlauben unterschiedliche Zeichenkettenlängen
- 8HerausforderungText im richtigen Speicher ersetzen
- 9HerausforderungBeide Dimensionen in einer Funktion zulässig halten
- 10ZusammenfassungDie Tabellenform sichtbar halten
5Eine Auswahl über die Kommandozeile auf Zulässigkeit prüfen
11 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungText vor seiner Deutung empfangen
- 2BeispielEinen Shell-Befehl Einträgen zuordnen
- 3ErklärungDie Anzahl vor der Konvertierung prüfen
- 4HerausforderungEinen Namen richtig gruppieren
- 5TippEine zurückgegebene Ganzzahl genügt nicht
- 6BeispielEine vollständige Konvertierung untersuchen
- 7ErklärungDen eigenen Wertebereich der Auswahl prüfen
- 8HerausforderungTeilweise numerisch aussehenden Text erkennen
- 9KernaussageSchreibweise und Wert sind getrennte Regeln
- 10HerausforderungGruppierung und numerische Prüfung verbinden
- 11ZusammenfassungVorhandensein, Konvertierung und Zweck prüfen
6Eine größenbegrenzte Eingaberoutine korrigieren
13 Schritte
Speichere diese Einheit, um jede Interaktive Lektion Schritt für Schritt durchzugehen.
- 1EinführungDen Schreibzugriff begrenzen und das Ergebnis prüfen
- 2BeispielDen zusätzlichen Schreibzugriff finden
- 3KernaussageEine zulässige letzte Position kann das Ende aufnehmen
- 4HerausforderungEinen aus einem Byte abgeleiteten Index prüfen
- 5ErklärungEin Wort mit Platz für das abschließende Nullzeichen lesen
- 6BeispielEinen größenbegrenzten Zeilenabschnitt lesen
- 7BeispielBeobachten, wo eine lange Zeile geteilt wird
- 8TippEinen Zeilenumbruch vor dem Entfernen untersuchen
- 9HerausforderungPlatz für eine vollständige Zeile einplanen
- 10BeispielEine Kopie vor dem Schreiben prüfen
- 11HerausforderungEin sicheres Präfix von einer akzeptierten Antwort unterscheiden
- 12VerbindungDiagnosewerkzeuge und Überlegungen zu Grenzen verwenden
- 13ZusammenfassungVier Prüfungen auseinanderhalten
Arrays und Zeichenketten in C: Übungsaufgaben
Set 1: Arraywerte, Schleifen und Funktionsaufrufe10 Übungsfragen
Frage 1 von 10
int a[5] = {6, 9}; bei a[3] gespeichert?Antwort zeigenAntwort ausblenden
Frage 2 von 10
int a[7]; deklariert, wobei int 4 Bytes belegt. Welches Paar gibt sizeof a und den letzten zulässigen Index an?Antwort zeigenAntwort ausblenden
Frage 3 von 10
int a[] = {-12, -4, -9}; zurückgeben?Antwort zeigenAntwort ausblenden
Frage 4 von 10
Was gibt dieser Code aus?
int a[] = {3, 8, 5, 2};
int sum = 0;
for (int i = 1; i < 4; i++) sum += a[i];
printf("%d", sum);
Antwort zeigenAntwort ausblenden
Frage 5 von 10
{2, 9, 7, 4} gibt beim ersten abweichenden Element -1 zurück. Wo sollte die Rückgabe bei Misserfolg erfolgen?Antwort zeigenAntwort ausblenden
Frage 6 von 10
{4, 7, 1, 8, 6} für i = 0 und i = 1 jeweils a[i] mit a[5 - 1 - i] getauscht wird?Antwort zeigenAntwort ausblenden
Frage 7 von 10
void set_first(int a[]) führt a[0] = 11; aus. Was enthält das erste Element des Arrays des Aufrufers nach dem Aufruf?Antwort zeigenAntwort ausblenden
Frage 8 von 10
void f(int a[], size_t n) belegen Zeiger 8 Bytes und Ganzzahlen 4 Bytes. Was ergibt sizeof a / sizeof a[0]?Antwort zeigenAntwort ausblenden
Frage 9 von 10
size_t für n und i. Sie muss ein Array rückwärts ausgeben und für n = 0 funktionieren. Welcher Parameter und welche Schleife erfüllen beide Anforderungen?Antwort zeigenAntwort ausblenden
Frage 10 von 10
a[4] aus int a[4] liest?Antwort zeigenAntwort ausblenden
Set 2: Zeichen, Zeichenkettenenden und UTF-810 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
'6' - '0'?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
('W' - 'A' + 5) % 26 + 'A'?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
c an isalpha übergeben werden, ohne ein negatives Byteargument zu riskieren?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
char color[] = "amber";?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
char s[8] = "planet"; s[3] = '\0'; s[0] = 'P'; übrig?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
char tag[4] = "east"; printf("%s", tag); unsicher?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
"m8n2p" und schreibt sie in ein ausreichend großes Ausgabearray. Was muss er nach dem letzten p schreiben?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
char s[] = "lamp"; um, indem es s[i] mit s[len - i] tauscht, solange i < len / 2 gilt. Dabei ist len gleich strlen(s). Wie lauten das abschließende strlen(s) und die unveränderte Kapazität?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
41 C3 B1 42 00. Ein Programm schreibt bei Byteindex 2 eine null. Wie lauten das neue strlen und die UTF-8-Gültigkeit des Textes?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 3: Bibliotheksoperationen auswählen und prüfen10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
"silver"?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
char label[10] = "blue"; strcat(label, "berry"); sein vollständiges Ergebnis speichern?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
char out[20]; strcat(out, "go");. Warum ist dies kein zulässiger Anfang für den Aufbau einer Zeichenkette?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
char *p = strrchr("archive.backup.bin", '.'); bei p?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
"on,,off;wait". Die Aufrufe verwenden strtok(s, ",;") und wiederholen dann strtok(NULL, ",;") bis NULL. Welche Token entstehen und bleibt s erhalten?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
char b[6]; int r = snprintf(b, sizeof b, "%s:%d", "job", 84); und was gibt der Aufruf zurück?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
"AB". Das Programm führt strncat(b, "123456", sizeof b - strlen(b) - 1) aus. Wie lauten der resultierende Text und seine gespeicherte Byteanzahl einschließlich null?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
char s[] = "wxyz"; memmove(s, &s[1], 4); als Zeichenkette?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 4: Tabellen, Textlisten und Eingaben beim Start10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
int table[4][6]?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
sizeof table[0] für int table[3][5]?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
{{2, 7, 4}, {6, 1, 5}}. Eine Schleife initialisiert sum einmal vor beiden Schleifen mit null, addiert jeweils eine Zeile und gibt nach jeder Zeile aus, ohne sum zurückzusetzen. Welche Werte werden ausgegeben und wie wird das korrigiert?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
char names[3][width], um „Iris“, „Magnolia“ und „Ash“ als vollständige ASCII-Zeichenketten zu speichern?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
char rows[2][5] = {"oak", "elm"}; und const char *refs[] = {"oak", "elm"};. Es muss das erste aufgeführte Wort durch den vollständigen Text „cedar“ ersetzen. Welches Vorgehen ist für die jeweilige Speicherform zulässig?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
./mark "West Room" 9 ausgeführt. Was sind argc und argv[2]?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 5: Eingabegrenzen und vollständige Ergebnisse10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
char word[18] eine Position für das abschließende Nullzeichen frei?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
"pine\n" in ein Array mit 10 Bytes. Wie lauten strlen des Ergebnisses und der Index seines abschließenden Nullzeichens?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
char b[6], ohne weitere Lesezugriffe dazwischen. Welchen Text speichert der zweite Aufruf?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
counts[c - 'a']++ bei ungeprüfter Zeicheneingabe unsicher?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
char out[6]. Auf den Aufruf snprintf(out, sizeof out, "%s%d", "R-", 1234) folgt if (r > sizeof out) reject();, wobei r der nicht negative Rückgabewert ist. Was wird gespeichert, weist der Test es zurück und was muss geändert werden?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
ERANGE zurück und verlangt *end == '\0', prüft aber nicht end == start. Welche unzulässige Eingabe kann dennoch als null akzeptiert werden?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
41 C3 A9 E2 82 AC 00. Ein Programm kopiert sie in char b[5] und reserviert die letzte Position für null. Wie lauten das resultierende strlen, die Anzahl der Anfangsbytes und die Gültigkeit der Kodierung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Set 6: Kombinierte C-Operationen nachvollziehen und korrigieren10 Übungsfragen
Speichere diese Einheit, um jede Antwort zu sehen und jedes Set zu üben.
Frage 1 von 10
int a[4] = {[2] = 9}; erstellt?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 2 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 3 von 10
int g[3][4] in zeilenweiser Speicherung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 4 von 10
./pick "" blue aus. Wie unterscheiden sich argv[1] und argv[argc]?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 5 von 10
int a[], size_t n und führt a[0] = 0; n = 0; aus. Der Aufrufer hat {5, 8, 2} und die Anzahl 3 übergeben und berechnet anschließend die ganzzahlige Summe seiner Elemente geteilt durch seine Anzahl. Welcher Mittelwert und welche Anzahl beim Aufrufer ergeben sich?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 6 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 7 von 10
strncpy(b, "planet", sizeof b); gefüllt. Das Programm verlangt das vollständige ursprüngliche Wort. Welche Korrektur erfüllt die Anforderung?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 8 von 10
int values[4] = {14, 28, 35, 49};. Es konvertiert „04“ mit dezimalem strtol vollständig, akzeptiert die Auswahlwerte 1–4 und liest dann values[selected]. Es muss alle vier Auswahlwerte erlauben und Auswahl 1 dem ersten Wert zuordnen. Was geschieht und welche Indexregel korrigiert es?Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 9 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Frage 10 von 10
Antwort zeigenAntwort ausblenden
Speichere diese Einheit, um diese Antwort zu sehen.
Hängst du bei deiner eigenen Aufgabe fest? Hausaufgabenhilfe
Schlüsselbegriffe
A
abschließendes Nullzeichen
text[2] = '\0' stoppen Zeichenkettenfunktionen nach den ersten zwei Zeichen in diesem Array.Abschneiden
Akkumulator
sum bei null ermöglicht es jedem sum += a[i], das nächste Arrayelement zur aktuellen Summe zu addieren.anwendungsspezifischer Wertebereich
Anzahl belegter Elemente
argc
argc gibt die Anzahl der an main übergebenen Argumentzeichenketten an, normalerweise einschließlich der Zeichenkette zum Starten des Programms../label red blue in einer normalen Shell ergibt für argc den Wert 3: den Startnamen und zwei Farben.argv
argv bietet Zugriff auf Argumentzeichenketten über Indizes; an der durch argc angegebenen Position folgt ein Nullzeiger.argc kann ein Programm mit strcmp(argv[1], "-v") seine erste Option erkennen.Array
int levels[6] reserviert sechs Ganzzahlen; eine Aktualisierung von levels[2] ändert den dritten gespeicherten Wert.Array variabler Länge
const int n = 6; int a[n]; ein Array variabler Länge, weil n kein konstanter Ausdruck ist.Array von Zeichenkettenzeigern
const char *names[] = {"Al", "Mina"} speichert zwei Verweise und erlaubt es, einen Verweis zu ersetzen.Arrayparameter
int a[] deklarierte Funktion benötigt weiterhin eine separate Länge, bevor sie ihre Eingabe mit einer Schleife durchlaufen kann.ASCII
'8' - '0' ergibt 8, weil die Codes der Ziffernzeichen aufeinanderfolgen.atoi
atoi konvertiert einen anfänglichen numerischen Teil in int, ohne Fehler zuverlässig zu melden, und zeigt bei Überlauf undefiniertes Verhalten.atoi den Wert 18; eine zurückgegebene Zahl belegt daher nicht, dass die gesamte Eingabe numerisch war.Ausgabearray
out[i] schreiben, während der Aufrufer den Besitz des Arrays behält.B
Bitmaske
0xC0 behält die beiden höchstwertigen Bits, sodass ein Programm das UTF-8-Fortsetzungsmuster erkennen kann.byteweiser Vergleich
strcmp einen lateinischen Buchstaben mit Akzent nach dem ASCII-Zeichen z ein, weil sein führendes Byte einen höheren Wert hat.C
C-Zeichenkette
char pet[] = "dog" initialisierte Array enthält drei Buchstaben, gefolgt von einem Zeichen mit dem Wert null.Caesar-Verschiebung
const-qualifizierter Parameter
const int a[], weil das Addieren der Eingabewerte keine Änderung dieser Werte erfordert.D
Dateiende
fgets kann dennoch erfolgreich sein, wenn die Funktion vor Erreichen dieses Zustands Zeichen gelesen hat.fgets-Puffer diesen letzten Text dennoch aufnehmen.E
Element
int a[3] = {8, 1, 6} enthält das Element a[1] den Wert 1 und kann eine andere Ganzzahl zugewiesen bekommen.Endzeiger
strtol den Wert 9 zurück, aber der Endzeiger verweist auf das nicht verarbeitete z.ERANGE
ERANGE kennzeichnet einen über errno gemeldeten Wertebereichsfehler, etwa ein für long zu großes oder zu kleines strtol-Ergebnis.errno vor der Konvertierung auf null und prüfe es dann auf ERANGE, bevor du eine lange Ziffernfolge akzeptierst.Escape-Sequenz
"A\nB" enthält einen Zeilenumbruch zwischen seinen Buchstaben; der umgekehrte Schrägstrich und n kodieren ein Zeichen.F
Feldbreite
%s-Konvertierung begrenzt die Anzahl der gespeicherten Eingabezeichen vor dem zusätzlichen abschließenden Nullbyte.char word[10] reserviert die Konvertierung %9s das zehnte verfügbare Byte für das abschließende Nullzeichen.fgets
fgets liest Zeileneingaben mit Größenbegrenzung, behält einen erreichten Zeilenumbruch und fügt ein abschließendes Nullzeichen hinzu, wenn sie erfolgreich Text liest.Filterung
Fortsetzungsbyte
10 und folgt innerhalb eines mehrbyteig kodierten Codepunkts auf ein führendes Byte.H
Häufigkeitsarray
counts[4]++ der Zählung dieser Ziffer ein Vorkommen hinzu.I
In-place-Transformation
Index
Initialisierer
int hits[4] = {3} initialisiert den ersten Zähler mit 3 und die anderen drei Zähler mit null.K
Kapazität
Kommandozeilenargument
L
leere Zeichenkette
char message[20] = "" stellt ein gültiges leeres Ziel für späteres Anhängen bereit, das in seine Kapazität passt.lineare Suche
{3, 8, 2} ist bei Index 2 erfolgreich, nachdem die früheren abweichenden Elemente geprüft wurden.Locale
toupper wandelt in der Locale „C“ keinen vollständigen UTF-8-Buchstaben mit Akzent in seine Großbuchstabenform um.M
memcmp
memcmp vergleicht eine feste Anzahl von Bytes und meldet ihre Reihenfolge. Sie prüft Speicherdarstellungen statt numerischer Werte.memcmp unterschiedliche gespeicherte Bytes findet.memcpy
memcpy kopiert genau eine angeforderte Anzahl von Bytes zwischen nicht überlappenden Bereichen, ohne Zeichenkettenenden zu interpretieren.memcpy(b, a, sizeof a) kopiert den gesamten Speicherinhalt von Array a, wenn b genug Platz hat und ein separater Bereich ist.memmove
memmove kopiert eine angeforderte Byteanzahl mit definiertem Verhalten, selbst wenn sich Quell- und Zielbereich überlappen.memmove.memset
memset füllt einen Bereich mit einem wiederholten Bytewert, ohne diesen numerischen Wert jedem Arrayelement einzeln zuzuweisen.memset(counts, 0, sizeof counts) setzt ein Ganzzahlarray auf null; der Bytewert 1 würde nicht die Ganzzahl 1 zuweisen.N
Nullzeiger
strchr(name, '.') != NULL, bevor du das Ergebnis als Anfang einer Dateinamenerweiterung verwendest.O
Off-by-one-Fehler
i <= n lässt einen bei null beginnenden Durchlauf durch n Elemente einen zusätzlichen Zugriff auf Index n versuchen.P
Parameter mit variabel modifiziertem Typ
int g[rows][cols] nach Parametern, die positive, passende Zeilen- und Spaltenanzahlen definieren.Puffer
Pufferüberlauf
R
Rückgabestatus
main signalisiert einen erfolgreichen Abschluss.S
size_t
size_t ist ein vorzeichenloser Ganzzahltyp für Objektgrößen, der von sizeof zurückgegeben wird; er wird häufig für Arraylängen verwendet.printf("%zu", sizeof data) verwendet das Größenformat, um die Anzahl der von data belegten Bytes auszugeben.snprintf
snprintf formatiert Text innerhalb einer angegebenen Kapazität und gibt die benötigte Länge des vollständigen Textes oder bei einem Fehler einen negativen Wert zurück.Spalte
g[r][c].g[r][c] liest.strcat
strcat hängt eine Quellzeichenkette am vorhandenen abschließenden Nullzeichen eines Ziels an und schreibt ein neues abschließendes Nullzeichen hinter den verbundenen Text.strchr
strchr findet das erste übereinstimmende Zeichen in einer Zeichenkette und gibt seine Position oder einen Nullzeiger zurück, wenn kein Treffer existiert.strchr findet den Rest „.part.ext“ einschließlich des ersten Punkts.strcmp
strcmp vergleicht abgeschlossene Zeichenketten als vorzeichenlose Bytes und gibt entsprechend ihrer Reihenfolge ein negatives Ergebnis, null oder ein positives Ergebnis zurück.strcmp("oak", "oak") == 0 prüft gleichen Inhalt, selbst wenn die beiden Zeichenketten getrennten Speicher belegen.strcpy
strcpy kopiert eine vollständige Quellzeichenkette und ihr abschließendes Nullzeichen in ein nicht überlappendes Ziel mit ausreichend verfügbarem Speicher.char out[5] deklariertes Ziel.strcspn
strcspn misst den Anfangsteil einer Zeichenkette, der keines der in ihrem zweiten Argument angegebenen Zeichen enthält.strcspn("fir:oak", ":,") gibt 3 zurück, weil das erste aufgeführte Trennzeichen nach den drei Buchstaben von fir auftritt.strlen
strlen durchsucht eine gültige Zeichenkette bis zu ihrem abschließenden Nullzeichen und gibt die Anzahl der vorangehenden Bytes als size_t-Wert zurück.char tag[30] = "oak" ist strlen(tag) gleich 3, während sizeof tag die volle Kapazität von 30 Bytes meldet.strncat
strncat hängt höchstens eine angegebene Anzahl von Quelltextbytes an und schreibt danach ein abschließendes Nullzeichen.strncmp
strncmp vergleicht höchstens eine angegebene Anzahl von Zeichenkettenbytes und stoppt bei einem Ende oder Unterschied früher.strncpy
strncpy kopiert bis zu einer Schranke und füllt kürzeren Text mit Nullen auf, schreibt aber kein abschließendes Nullzeichen, wenn die Quelle diese Schranke ausschöpft.strrchr
strrchr findet das letzte übereinstimmende Zeichen in einer Zeichenkette und gibt seine Position oder einen Nullzeiger zurück, wenn kein Treffer existiert.strrchr das Trennzeichen der Erweiterung finden kann.strtol
strtol konvertiert Text mit einer gewählten Basis in long und meldet eine Endposition sowie Informationen zu Wertebereichsfehlern.strtol(text, &end, 10) ermöglicht dem Programm, einen verbleibenden Buchstaben nach ansonsten gültigen Dezimalziffern zurückzuweisen.T
Teilzeichenkette
strstr findet ihre erste übereinstimmende Anfangsstelle.Tokenisierung
strtok verändert dazu die Eingabe und merkt sich eine Position.strtok(NULL, ",") setzt die Zerlegung derselben beschreibbaren Eingabe fort, statt sie neu zu beginnen.Trennzeichen
U
undefiniertes Verhalten
Unicode-Codepunkt
unvollständige Zeile
fgets-Abschnitten ankommen, sodass der zweite Abschnitt nicht zu einer neuen Antwort werden sollte.UTF-8
V
Verkettung
verschachtelte Schleifen
W
Wortgrenze
Wraparound bei vorzeichenlosen Ganzzahlen
size_t-Rückwärtszähler kann nicht durch einen negativen Wert stoppen; verwende i > 0 und greife auf Position i - 1 zu.Z
Zahlenkonvertierung
% 26 reduzieren kann.Zeichenkettenlänge
Zeichenkettenliste mit fester Zeilenlänge
char labels[5][12] hat jede Bezeichnung zwölf Bytes Speicher, auch wenn ihr aktueller Text kürzer ist.Zeichenkettenliteral
const char *label = "ready" verweist auf ein Literal; eine beschreibbare Kopie kann char label[] = "ready" verwenden.Zeichenklassifikation
<ctype.h>, ob ein Zeichencode zu einer Kategorie wie Ziffer, Buchstabe oder Leerraumzeichen gehört.isdigit((unsigned char) c) ergibt einen Wert ungleich null, wenn das gespeicherte Byte eine Dezimalziffer darstellt.Zeichenkodierung
Zeichenkonstante
int, der einen Zeichencode darstellt, etwa den Code eines Buchstabens.printf("%c %d", 'B', 'B') den Buchstaben B und den numerischen Wert 66 an.Zeile
int table[2][5] bezeichnet table[1] die zweite Zeile, die fünf Ganzzahlpositionen enthält.Zeilenbreite
int g[][6] nimmt Zeilen mit sechs Ganzzahlen auf, während der Aufrufer die Zeilenanzahl separat angibt.zeilenweise Speicherung
grid[0][3] im Speicher einer Tabelle mit vier Spalten ist grid[1][0].Zugriff außerhalb der Speichergrenzen
a[8] in einem Array mit acht Elementen ist unzulässig, selbst wenn ein Testlauf scheinbar einen brauchbaren Wert zurückgibt.Zuweisungsanzahl
scanf erfolgreich abgeschlossen hat. Sie dient zur Prüfung, ob angeforderte Daten tatsächlich gelesen wurden.zweidimensionales Array
int board[4][6] stellt 24 Ganzzahlen bereit, die über vier zulässige Zeilenindizes und sechs zulässige Spaltenindizes erreicht werden.Über diese Einheit
Erstelle deine eigenen Lernmaterialien
Mach aus deinen Notizen, Folien und Dokumenten Lernleitfäden, Übungsfragen, Karteikarten und 6 weitere Tools.