strtok aus Galileo Computing C von A bis Z

Gast #1186148
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 ?
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.
Gast
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 ...
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…
Gast
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.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 17 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.