Forum: Compiler & IDEs strtok aus Galileo Computing C von A bis Z


von gert (Gast)


Lesenswert?

im buch 'C von A bis Z' steht folgendes geschrieben:

---------------------------------------------
1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#define MAX 255
5
char *eingabe(char *str) {
6
   char input[MAX];
7
   printf("Bitte \"%s\" eingeben: ",str);
8
   fgets(input, MAX, stdin);
9
   return strtok(input, "\n");
10
}
11
int main(void) {
12
   char *ptr;
13
   ptr = eingabe("Vorname");
14
   printf("Hallo %s\n", ptr);
15
   ptr = eingabe("Nachname");
16
   printf("%s, interssanter Nachname\n", ptr);
17
   return EXIT_SUCCESS;
18
}

Der Funktion eingabe() wird hierbei als Argument die Adresse eines 
Strings übergeben. In der Funktion werden Sie aufgefordert, einen Namen 
einzugeben. Die Anfangsadresse des Strings geben Sie mit folgender Zeile 
zurück:
1
return strtok(input, "\n");

Die Funktion strtok() liefert ja selbst als Rückgabewert einen 
char-Zeiger zurück. Da die Funktion fgets() beim Einlesen von der 
Standardeingabe das Newline-Zeichen mit einliest, haben Sie hierbei 
gleich zwei Fliegen mit einer Klappe geschlagen. Das Newline-Zeichen 
wird mit strtok() entfernt, und die Funktion liefert auch gleich die 
Anfangsadresse des Strings input als Rückgabewert zurück, welchen Sie 
gleich mit return weiterverwenden. Doch Vorsicht, Folgendes funktioniert 
nicht:
1
char *eingabe(char *str) {
2
   char input[MAX];
3
   printf("Bitte \"%s\" eingeben: ", str);
4
   fgets(input, MAX, stdin);
5
   return input;
6
}

Normalerweise sollte hier der Compiler schon meckern, dass etwas nicht 
stimmt. Spätestens aber, wenn Sie das Beispiel ausführen, werden Sie 
feststellen, dass anstatt des Strings, den Sie in der Funktion eingabe() 
eingegeben haben, nur Datenmüll ausgegeben wird.
---------------------------------------------

die strtok funktion liefert ja auch nur einen pointer auf den 
übergebenen string , dh. der test mittels return strtok(input, "\n"); 
sollte auch nur datenmüll liefern ?

von Karl H. (kbuchegg)


Lesenswert?

gert wrote:

>
1
char *eingabe(char *str) {
2
>    char input[MAX];
3
>    printf("Bitte \"%s\" eingeben: ", str);
4
>    fgets(input, MAX, stdin);
5
>    return input;
6
> }
>
> Normalerweise sollte hier der Compiler schon meckern, dass etwas nicht
> stimmt. Spätestens aber, wenn Sie das Beispiel ausführen, werden Sie
> feststellen, dass anstatt des Strings, den Sie in der Funktion eingabe()
> eingegeben haben, nur Datenmüll ausgegeben wird.

Das Problem an dieser Stelle ist, dass hier ein Pointer auf eine lokale 
Variable (nämlich input) zurückgegeben wird. Lokale Variablen werden 
aber beim Verlassen einer Funktion zerstört. Damit liefert die Funktion 
einen Pointer auf eine Variable die nicht mehr existiert.

> die strtok funktion liefert ja auch nur einen pointer auf den
> übergebenen string , dh. der test mittels return strtok(input, "\n");
> sollte auch nur datenmüll liefern ?

Genau. Der retournierte Pointer ist im Prinzip immer noch ein Pointer 
auf input. strtok ändert daran nichts. Wenn die angegebene Lösung als 
Musterlösung präsentiert wird, dann ist diese Musterlösung fehlerhaft.

von gert (Gast)


Lesenswert?

ok danke - ja im buch wird dies tatsächlich als lösung vorgeschlagen, 
und da ja strtok zb keinen neuen speicher vom heap anfordert (als return 
value ), kann dies nicht gehen ...

von Εrnst B. (ernst)


Lesenswert?

Die übliche Quick-n-Dirty Lösung wäre, einfach das input-array als 
static zu deklarieren, dann bleibt der Pointer gültig.

Ist aber keine schöne Lösung, spätestens wenn jemand das ganze mit 
Threads verwenden will fliegt er auf die Schnauze…

von tuppes (Gast)


Lesenswert?

Ja ja, Zeichenketten in C, immer wieder leidiges Thema.

Am besten, man verabschiedet sich von dem Dogma, dass das Ergebnis als 
Returnwert zurückgegeben werden muss, und baut etwas in dieser Art:
1
void eingabe(char * input, const char *message) {
2
   printf("Bitte \"%s\" eingeben: ", message);
3
   fgets(input, MAX, stdin);  // Achtung: Buffer-Overrun-Check fehlt hier!
4
}

Das Ergebnis wird über einen Aufrufparameter herausgereicht, den 
Speicher dafür stellt der Aufrufer zur Verfügung und weiß daher auch, 
ob der Speicher ggfs. wieder weggeräumt werden muss.

Das Beispiel ist natürlich nur als Demonstration gedacht. Es prüft nicht 
auf Buffer Overrun (muss unbedingt gemacht werden!).

Außerdem ists vom Design her etwas merkwürdig, dass eine Routine, die 
"eingabe" heißt, als allererstes eine Ausgabe macht. Aber ich wollte 
nicht zu sehr von der Vorlage abweichen.

--------------------------------------------------

Ein anderer Ansatz wäre, eine Art "String-Objekt" zu definieren, dass 
return-fähig ist und das ganze Speichermanagement intern kapselt. Aber 
das geht über Anfängerstoff eindeutig hinaus.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.