Menu

String zu Int in C: atoi, strtol und Zahlen in Text umwandeln

Zwischen Text und Zahlen umwandeln in C: warum atoi keinen Fehler melden kann, wie man strtol und strtod mit endptr und errno richtig benutzt, Zahlen mit snprintf in Zeichenketten verwandelt, und Tricks mit einzelnen Ziffern und '0'.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Text kommt von Benutzern, Dateien und der Kommandozeile als Zeichen an. "42" sind drei Bytes - '4', '2', '\0' - und nichts daran ist die Zahl 42, bis du es umwandelst. C bietet zwei Generationen von Umwandlungsfunktionen: die kurzen, alten, die keine Fehler melden können, und die längeren, die es können. Diese Seite zeigt beide und erklärt, warum echte Programme den zweiten Satz benutzen.

Die alte Familie: atoi, atol, atof

Sie lesen sich wie ihre Namen: ASCII nach int, nach long, nach double. Ein Argument, ein Ergebnis.

Führender Leerraum wird übersprungen, ein optionales Vorzeichen gelesen, Ziffern verbraucht, und alles danach ignoriert. Das ist bequem, bis die Eingabe falsch ist:

Alle drei geben 0 aus. Es gibt keinen Rückgabecode, kein Flag, keine Möglichkeit zu fragen „hat das geklappt?" - und ein Wert, der int überschreitet, ist undefiniertes Verhalten statt eines gemeldeten Fehlers. Für ein fest eingetragenes Literal in einem Spielzeugprogramm ist das erträglich. Für alles, was eine Person oder eine Datei liefern kann, ist es ein stiller Fehlererzeuger.

Das richtige Werkzeug: strtol

strtol (string to long) ist dieselbe Umwandlung mit zwei Kanälen, um zu melden, was geschah.

long strtol(const char *text, char **endptr, int base);

endptr wird auf das erste Zeichen gesetzt, das die Funktion nicht verbraucht hat. base ist die Zahlenbasis - nimm 10, oder 0, um das Präfix des Literals entscheiden zu lassen (0x für hexadezimal, führende 0 für oktal).

endptr zu lesen beantwortet die Fragen, die atoi nicht kann:

  • end == input bedeutet, dass nichts umgewandelt wurde - der Text begann nicht mit einer Zahl.
  • *end != '\0' bedeutet, dass dahinter noch Inhalt stand. Ob das ein Fehler ist, entscheidest du: Beim Auswerten von "42 Aepfel" ist es in Ordnung und ein Problem, wenn die ganze Zeile eine Zahl hätte sein sollen.

Ein Überlauf wird separat über errno gemeldet. Das vollständige, korrekte Idiom lohnt sich einmal zu schreiben und wiederzuverwenden:

Drei Einzelheiten machen das korrekt. errno = 0 vor dem Aufruf zählt, weil die Bibliothek errno nur je setzt; sie löscht es nie, ein alter Wert aus einem früheren Aufruf würde also als Fehlschlag dieses Aufrufs gelesen. strtol liefert long, das breiter als int sein kann, die Bereichsprüfung gegen INT_MIN/INT_MAX ist also von ERANGE getrennt. Und den Erfolg als Status zu liefern, während der Wert über einen Zeiger übergeben wird, ist die Art, wie C-Funktionen „entweder ein Wert oder ein Fehlschlag" melden - der Rückgabewert kann nicht beide Aufgaben erfüllen.

strtoul ist das vorzeichenlose Gegenstück, und strtoll/strtoull behandeln long long.

Andere Basen

Basis 16 liest hexadezimal; Basis 0 prüft das Präfix und entscheidet selbst.

Basis 0 ist in einem bestimmten Fall eine Falle: Eine mit Nullen aufgefüllte Dezimalzahl wie "012" wird als oktal gelesen und kommt als 10 heraus. Ist deine Eingabe dezimal, sag ausdrücklich 10.

Gleitkommazahlen: strtod

strtod hat dieselbe Form für double, und atof hat denselben Makel wie atoi.

strtod akzeptiert auch wissenschaftliche Notation ("1.5e3"), hexadezimale Gleitkommazahlen und die Wörter "inf" und "nan". Die Locale beeinflusst das Dezimaltrennzeichen, was Leute überrascht, die anderswo geschriebene Dateien auswerten - in der Standard-Locale „C" ist das Trennzeichen immer ein Punkt.

Zahlen zu Text: snprintf

In die andere Richtung ist snprintf die Standardantwort. Es schreibt höchstens die Größe, die du ihm gibst, terminiert immer und liefert die Länge, die das vollständige Ergebnis gebraucht hätte.

Der Rückgabewert ist die Abschneideprüfung. Ist er größer oder gleich der Puffergröße, passte der Text nicht:

Zwei Dinge, zu denen man nicht greifen sollte: itoa gehört nicht zum Standard-C, Code, der es benutzt, kompiliert also nicht überall; und sprintf ist snprintf ohne die Größenbegrenzung, was bedeutet, dass es kommentarlos über das Ende deines Puffers hinausschreibt.

Einzelne Zeichen und '0'

Die Ziffernzeichen '0' bis '9' liegen garantiert aufeinanderfolgend, schlichte Subtraktion wandelt also zwischen einem Ziffernzeichen und seinem Wert um.

Nur Ziffern verhalten sich so - Buchstaben müssen nicht aufeinanderfolgen, c - 'a' ist für Alphabetarithmetik also nicht portabel. Sichere die Subtraktion mit isdigit ab, bevor du ihr traust:

Der Cast nach unsigned char ist aus demselben Grund nötig wie bei Zeichenketten: Die Funktionen aus <ctype.h> sind für negative Argumente undefiniert, und ein schlichtes char kann vorzeichenbehaftet sein.

Welche Funktion man nimmt

  • Text von einem Benutzer, einer Datei oder aus argvstrtol / strtod mit der vollen Prüfung.
  • Ein selbst geschriebenes Literal, das du kontrollierst → atoi ist akzeptabel, wenn auch unspektakulär.
  • Zahl in einen Puffer → snprintf, und prüfe den Rückgabewert.
  • Ein einzelnes Ziffernzeichen → '0' abziehen, nach isdigit.
  • Niemals → gets, sprintf, itoa.

Häufig gestellte Fragen

Wie wandelt man in C eine Zeichenkette in ein int um?

strtol(text, &end, 10) ist die richtige Antwort: Es liefert den Wert, setzt end auf das erste nicht verbrauchte Zeichen und meldet einen Überlauf über errno. atoi(text) ist kürzer, liefert aber 0 sowohl für "0" als auch für "hallo", ohne dass man beides unterscheiden könnte.

Was ist an atoi in C falsch?

Es kann keinen Fehlschlag melden. atoi("abc") liefert 0, was von einer echten Null nicht zu unterscheiden ist, und ein Überlauf ist undefiniertes Verhalten statt eines gemeldeten Fehlers. Nimm strtol, wann immer der Text von einem Benutzer, einer Datei oder einem Kommandozeilenargument kam.

Wie wandle ich in C ein int in eine Zeichenkette um?

snprintf(buf, sizeof buf, "%d", n). Es ist Standard, durch die Puffergröße begrenzt und liefert die Anzahl der Zeichen, die das vollständige Ergebnis gebraucht hätte - ein Rückgabewert ab sizeof buf sagt dir also, dass der Text abgeschnitten wurde. itoa ist kein Standard-C.

Wie mache ich in C aus dem Zeichen '7' die Zahl 7?

Zieh '0' ab: int digit = c - '0';. Die Ziffernzeichen liegen in jedem C-Zeichensatz aufeinanderfolgend, die Subtraktion ergibt also 0 bis 9. Sichere es zuvor mit isdigit((unsigned char)c) ab, denn für alles andere ist die Rechnung bedeutungslos.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S