display_error(errno); // Fehlernummer existiert
Denn Kommentare mit /*
und */
beziehungsweise mit //
ignorieren sich gegenseitig.
Abbildung 3.2: Die Tücken verschachtelter Kommentare
Verschachtelte Kommentare sind nervig, aber Sie müssen sich jetzt noch keine Gedanken darüber machen.
fgets und puts
Die printf
- und die scanf
-Funktion sind natürlich nicht der einzige Weg, um Text aus- oder einzugeben. Dreimal Nein. Die Sprache C ist voll mit Tricks, um dieses Ziel zu erreichen. Und wenn Sie herausfinden, wie begrenzt und lahm diese sind, dann werden Sie Ihre eigenen Funktionen zum Lesen von Tastatureingaben und zur Ausgabe von Informationen schreiben wollen. Aber bis dahin müssen Sie mit den Bordmitteln leben.
Ein weiterer simpler Weg zur Ein- und Ausgabe ist die Verwendung der Funktionen fgets
und puts
aus der s
-Familie.
Und tschüss scanf, willkommen fgets
fgets
ist nett und einfach. scanf
ist im Vergleich dazu undurchsichtig. Dennoch machen beide Ähnliches: Zeichen von der Tastatur einlesen und in einer Variablen speichern. fgets
liest allerdings nur Text ein, scanf
kann auch numerische Werte und verschiedene Stringarten einlesen.
Das Format von fgets
können Sie im Kasten »Die Funktion fgets« sehen.
Ein unfreundliches Programmbeispiel
Es folgt nun das Programm Insult1.c
. Das Programm ist praktisch identisch mit dem Programm Whoru.c
.
#include <stdio.h>
int main()
{
char idiot[21];
printf("Name eines Idioten: ");
scanf("%s", idiot);
printf("Yep, ich denke auch, dass %s ein Idiot ist.\n", idiot);
return 0;
}
Listing 3.7: Name: Insult1.c
Geben Sie Insult1.c
in Ihrem Editor ein, kompilieren und starten Sie das Programm. Bei Fehlern vergleichen Sie die genaue Schreibweise, Anführungszeichen und Semikolons.
Die Ausgabe sieht dann so aus:
Name eines Idioten: Karl Klammer
Yep, ich denke auch, dass Karl Klammer ein Idiot ist.
Mit scanf
wird die Eingabe gelesen. Egal, was eingelesen wird, die printf
-Anweisung kann es ausgeben.
Das finstere Geheimnis der Sprache C
Eine dramatische Überschrift – aber was hat scanf
damit zu tun? Sicherlich haben Sie schon oft in Artikeln von Speicherüberläufen oder sogenannten Buffer-Overflows gehört.
Nun, daran ist scanf
nicht immer unbeteiligt. Aber was hat es dazu beigetragen? In der Sprache C muss sich der Programmierer um alles selbst kümmern. Starten Sie wieder Insult1.c
, aber geben Sie einen wirklich langen Namen ein – so wie Donaudampfschifffahrtsgesellschaftseignergattinskindeskinder
.
Bei mir stürzt dann das Programm ab.
Sollte sich der Effekt bei Ihnen nicht gleich einstellen, tippen Sie einfach einen noch längeren Namen ein. Wann es genau passiert, ist vom Betriebssystem und vom Compiler abhängig.
Was ist passiert? Erinnern Sie sich: Mit idiot[21]
haben wir Speicher für 20 Zeichen (und Nullbyte) reserviert. Und nun füttern Sie aber scanf
mit viel mehr Zeichen. Wohin soll er das alles packen? Und genau das ist das Problem. scanf
bekommt den Hals nicht voll genug, und liest immer weiter Zeichen ein. Diese werden einfach immer weiter in den Speicher geschrieben, bis irgendwann etwas kaputtgeht – der Programmabsturz ist unausweichlich.
Das klingt zwar harmlos, ist aber eine der großen Sicherheitslücken im Zeitalter des Internets geworden und hat auch den Ruf der Sprache C erheblich beschädigt, denn C ist sehr anfällig dafür.
Durch clevere Eingaben gelingt es Hackern hiermit sogar, Maschinencode in den Speicher zu schmuggeln, der dann ausgeführt wird. Auf diese Weise kann man den Computer fernsteuern oder eigene Programme starten.
Sichere Dateneingaben
Was jetzt? Müssen wir deshalb aufhören, die Sprache C zu benutzen? Nein, Sie müssen einfach nur damit aufhören, Funktionen wie scanf
zu verwenden. Es gibt eine sichere Alternative dazu: den großen Bruder fgets
. Da ist auch das f
wieder. Schauen Sie sich zunächst das Programm InsultSicher.c
an.
#include <stdio.h>
int main()
{
char idiot[20];
printf("Name eines Idioten: ");
fgets(idiot, 20, stdin);
printf("Yep, ich denke auch, dass %s ein Idiot ist.\n", idiot);
return 0;
}
Listing 3.8: Name: InsultSicher.c
Es fällt sofort auf, dass fgets
als zweiten Parameter die Zahl 20
verwendet. Die kennen wir schon – es ist die Maximallänge von unserem String idiot
mitsamt diesem famosen Nullbyte. Damit verhindert fgets
, dass mehr Zeichen eingelesen werden können, als Platz vorhanden ist, und Speicherüberläufe wie bei scanf
sind damit ausgeschlossen.
Die Funktion fgets
Die Funktion fgets
liest über die Tastatur eingegebene Zeichen ein und speichert diese in einer Stringvariablen, die bereits zuvor über das Schlüsselwort char
erzeugt worden sein muss. Die Funktion hört mit dem Lesen von Zeichen auf, sobald die
length
Zeichen in den String gelesen. War der String kürzer als length
und wurde die -Taste gedrückt, so ist das Zeichen \n
ebenfalls im String enthalten.
Der String wird von einem Nullbyte beendet.
fgets(var, length, stdin);
fgets
folgt ein Paar runde Klammern, dahinter ein Semikolon, dazwischen der Name einer Stringvariablen, die Anzahl der Zeichen, die Sie lesen wollen – und eine Variable mit dem Namen stdin
.
stdin
heißt Standard Input und bezeichnet den Zeichenstrom, der von der Tastatur in das Programm fließt (sehr poetisch).
Die fgets
-Funktion ist eine vollständige Anweisung in C. Sie endet daher auch immer mit einem Semikolon.