Die Programmargumente in argv sind stets Strings, oftmals möchte man diese aber als numerische Werte betrachten. Im Folgenden wollen wir uns deshalb mit Funktionen beschäftigen, die eine entsprechende Konvertierung verlässlich ermöglichen.
Warum ist die Verwendung der Funktionen atol/atoi/atof zu diesem Zweck problematich? Ziehen Sie auch die Man-Pages heran.
Bei diesen Funktionen ist es nicht möglich, Fehler (z.B. invalide Zeichen) zu erkennen — im Fehlerfall geben diese Funktionen 0 zurück, was auch eine korrekte Eingabe sein kann.
Betrachten Sie nun die Funktionen strtol/strtoul/strtod, welche die Parameter const char* nptr und char** endptr entgegen nehmen. Welche Bedeutung haben diese?
nptr ist der String, der konvertiert werden soll.
endptr ist ein Ausgabeparameter. An das Ziel des Pointers wird ein Pointer auf den ersten char in nptr gesetzt, der nicht konvertiert werden konnte. (Wenn endptr == NULL, wird nichts geschrieben.)
Beispiel:
const char* str = "123x9";
char* endptr; // uninitialized, value set by strtol
long res = strtol(str, &endptr, /*base=*/10);
// result of conversion until first invalid character
assert(res == 123);
// endptr points to the first invalid character, 'x'.
assert(endptr == str + 3);
Überlegen Sie sich, wie Sie folgende Fehlerfälle erkennen können:
Der String enthält für eine Zahl ungültige Zeichen.
Wenn endptr nicht auf das \0-Terminal zeigt oder auf den Anfang des Strings (der String könnte gar keine Zahl enthalten), war die Konvertierung nicht gänzlich erfolgreich.
Die im String enthaltene Zahl passt nicht in den Wertebereich des Ziel-Datentyps.
In diesem Fall wird errno auf ERANGE gesetzt. Da errno im Erfolgsfall nicht gesetzt wird, muss vor der Konvertierung errno = 0 gesetzt werden.
Passen Sie folgendes C-Programm so an, dass die Paramter in argv der Form <double>,<double> mittels strtod in zwei double-Werte aufgeteilt wird. Nutzen Sie endptr, um den Beginn des zweiten Wertes zu finden. Falls ein Parameter fehlerhaft ist, soll error ausgegeben werden und mit dem nächsten Argument fortgefahren werden.
#include <math.h>
#include <stdio.h>
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
double x = 0, y = 0; // TODO: parse from argv[i]
printf("%f\n", pow(x, y));
}
return 0;
}
Beispielaufruf:
$ ./strtonum 2,4 2,2e10 1x5,4 3, 2 "3, 4" .5,1e400 0x1.8p4,2
16.000000
inf
error
error
error
81.000000
error
576.000000
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
char *endx, *endy;
errno = 0;
double x = strtod(argv[i], &endx);
if (endx == argv[i] || *endx != ',' || errno == ERANGE) {
error:
puts("error");
continue;
}
double y = strtod(endx + 1, &endy);
if (endy == endx + 1 || *endy != '\0' || errno == ERANGE)
goto error;
printf("%f\n", pow(x, y));
}
return 0;
}