C - fgets - verständnisproblem

#417597
Lesenswert?

hallo leute,

folgenden prozedur liefert auf grund da input nur " 51 " ist müll
zurück!
darunter findet ihr dann ein beispiel welches läuft, aber ich verstehe
fgets anscheinend nicht richtig! warum funktionieren nicht beide?
denn der wert wie gross der string ist, sollte doch mir überlassen
sein! woran scheitert es?

BSP1.:
--------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 51

char *eingabe(char *str){
     char input[MAX];

     printf("bitte \"%s\" eingeben!\n",str);
     fgets(input, MAX, stdin);
     return strtok(input, "\n");
}

int main(void){
    char *ptr;

    ptr = eingabe("vorname");
    printf("hallo %s\n", ptr);

getchar();
return EXIT_SUCCESS;
}

BSP2.:
---------------------------------------------------------------

diese hier bei welcher MAX den wert 255 (also sehr gross zum obigen
beispiel wo der wert nur auf 51 war) hat, läuft korrekt!


/* ptr13.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 255
char *eingabe(char *str) {
   char input[MAX];
   printf("Bitte \"%s\" eingeben: ",str);
   fgets(input, MAX, stdin);
   return strtok(input, "\n");
}
int main(void) {
   char *ptr;
   ptr = eingabe("Vorname");
   printf("Hallo %s\n", ptr);
   ptr = eingabe("Nachname");
   printf("%s, interssanter Nachname\n", ptr);
   return EXIT_SUCCESS;
}

WARUM laufen nicht beide programme einwandfrei?

danke schon im voraus für eure hilfe

>Raphael
#417600
Lesenswert?

ich weiss was ihr meint!

jedoch, da das programm funktioniert (zumindest programm nummer 2, in
welchem MAX 255 gross ist, wobei ich nicht verstehe warum programm 1
nicht funktioniert in dem MAX relativ klein 51 ist) kann anscheinend
doch auf die variable auch nach ende des funktionsaufrufes
zurückgegriffen werden!!!

das programm BSP2.: stammt aus dem buch c von a bis z.

ich weiss, dass normalerweise auf eine
Gast #417603
Lesenswert?

Hi,

> dürfte ein komischer zufalle gewesen sein, dass BSP2 auch so
> richtig gelaufen ist!!!

Ja, das war Zufall. Sowas klappt nur dann, wenn die Information noch
auf Stack vorhanden ist, worauf man sich aber nie verlassen kann und
auch nicht sollte.

Grüße

mplusplus
#417604
Lesenswert?

ALLES FALSCH

jetzt hab ichs!!!

in diesem beispiel wurde ein Zeiger als Argument übergeben!
darum muss die variable "char input[MAX]" nicht als STATIC oder
GLOBAL deklariert sein!

Die Funktion eingabe() gibt eben einen solchen Speicherbereich (lokales
Feld) zurück, der sich ebenfalls auf diesem Stack-Frame befindet bzw.
befand – und somit bei Beendigung der Funktion nicht mehr vorhanden
ist. Wollen Sie also einen Zeiger auf einen Speicherbereich
zurückgeben, haben Sie folgende Möglichkeiten. Sie verwenden ...

-einen statischen Puffer (static).
-einen beim Aufruf der Funktion als Argument übergebenen Puffer.
-einen mittels malloc() reservierten Speicher

Bsp.:
Hierzu ein Beispiel, welches alle drei Möglichkeiten demonstrieren
soll:

/* ptr14.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Fehler: Funktion gibt die Adresse
 * einer lokalen Variablen zurück */
char *test1(void){
   char buffer[10];
   strcpy(buffer, "testwert");
   return buffer;
}
/* Möglichkeit1: Statische Variable */
char *test2(void){
   static char buffer[10];
   strcpy(buffer, "testwert");
   return buffer;
}
/* Möglichkeit2: Speicher vom Heap verwenden */
char *test3(void){
   char *buffer = (char *) malloc(10);
   strcpy(buffer, "testwert");
   return buffer;
}
/* Möglichkeit3: Einen Zeiger als Argument übergeben */
char *test4(char *ptr){
   char buffer[10];
   ptr = buffer;
   strcpy(buffer, "testwert");
   return ptr;
}
int main(void) {
   char *ptr;
   ptr = test1();
   printf("test1: %s\n", ptr); // Datenmüll
   ptr = test2();
   printf("test2: %s\n", ptr);
   ptr = test3();
   printf("test3: %s\n", ptr);
   test4(ptr);
   printf("test4: %s\n", ptr);
   return EXIT_SUCCESS;
}

das war nun die erklärung/lösung meines problems!!!

lg >Raphael
Persönliche Seite #417605
Lesenswert?

Nein, Du hast es nicht.

Möglichkeit 4 unterscheidet sich inhaltlich nicht von der fehlerhaften
Möglichkeit 1, denn der Puffer, auf den der Pointer verweist, ist nach
wie vor ein lokaler Puffer. Und ist damit ebenso falsch.

Mäßig korrekt sind somit nur 2 und 3, wobei bei 3 nach Aufruf der
Funktion tunlichst der per malloc angeforderte Speicher auch
wiederfreigegeben werden sollte, sonst gibt es Speicherlecks.
#417607
Lesenswert?

> das programm BSP2.: stammt aus dem buch c von a bis z.

Ich hoffe es ist dort als abschreckendes Beispiel gezeigt, das
verdeutlicht das 'undefiniertes Verhalten' (so heist das in
der C-Fachsprache) auch bedeuten kann: "Sieht auf den ersten
Blick so aus als ob es gehen würde, ist aber trotzdem falsch."
#417608
Lesenswert?

hi leute,

ist in der online fassung und in meinem buch gleich! also wenn es
tatsächlich ein fehler ist, dann in allen versionen!

ich hab ja den "fehler" nicht gefunden! den habt ja ihr gefunden :-)

leute ich weiss was undefiniertes verhalten bedeutet... das einzige was
ich nicht kann ist anständig zu programmieren!!! darum arbeite ich ja
hart daran :-)

es war mich auch unklar, aber ich dachte durch

return strtok(variable, "\n"); /* auch wenn sie "nur" in der
aufgerufenen funktion deklariert ist, bleibt der speicher erhalten!
... da jürgen wolf in seinem buch ja auch extra angegeben hatte EINEN
ZEIGER ALS ARGUMENT ÜBERGEBEN .. dachte ich dies ist eine andere
vorgehensweise!

nun gut, ich werde jürgen wolf darauf aufmerksam machen
mal sehn was er dazu sagt...

:-) in zukunft werd ich mit static oder GLOBAL arbeiten!!!

danke leute

>Raphael
Gast #417609
Lesenswert?

Warte noch! Es ist deutlich als ein abschreckendes Beispiel
gekennzeichnet, so wie Karl Heinz Buchegger gemeint hat!

Auszug aus dem Text:

"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."

http://www.pronix.de/pronix-743.html
#417611
Lesenswert?

ja leute war mein fehler, sehr peinlich

ich dachte er meinte als abschreckendes beispiel folgenden auszug
welcher kurz nach dem eigentlich programm kam

char *eingabe(char *str){
     char input[MAX];

     printf("Bitte \"%s\" eingeben: ", str);
     fgets(input, MAX, stdin);
     return input;
}

tut mir total leid leute
ich hab mir diese zeilen auch gelesen... nur ich dachte er meinte das
folgende

ich tip die listen chronologisch ab, und das buch find ich auch sehr
gut und ausführlich, nur er gibt oft negativbeispiele an... in diesem
fall hab ichs nicht erkannt!!!

sorry leute!!!

bin normal vorsichtig beim posten
Gast #417612
Lesenswert?

@Stefan

dieser Satz bezieht sich auf den Fall, dass die lokal definierte
Zeichenkette input direkt als Rückgabewert verwendet wird, also ohne
strtok().

Kann es vieleicht sein, dass strtok() den extrahierten String selber
nochmal als static speichert und den Zeiger auf diese (noch gültigen
Daten) zurückgibt? strtok() verwendet ja je nach Übergabewerten Daten
aus vorherigen Aufrufen. Ein Link dazu:
http://ivs.cs.uni-magdeburg.de/bs/lehre/wise0102/usp/seminare/seminar3.shtml

Grüße

mplusplus
#417613
Lesenswert?

das hab ich ja anfangs auch vermutet!!!

aber da anscheinend BSP2 zufällig richtig läuft und BSP1 nicht
denke ich nicht dass strtok() den extrahierten string selber nochmal
als static speichert!

aber ich weiss es nicht...

ich sollt mich mit was leichterem befassen!!!

soweit bin ich wie man merkt noch nicht :-)
Gast #417614
Lesenswert?

Diese Beschreibung ist nicht eindeutig. Ich würde hierzu empfehlen nach
mehr Beschreibungen und Codebeispielen zu suchen.

Ich lese die Beschreibung in der Weise, dass strtok() sich die Adresse
des Strings merkt und mit der gemerkten Adresse arbeitet, solange wie
als erstes Argument NULL übergeben wird.

Ich entnehme das aus dem Hinweis, dass der übergebene Puffer
manipuliert wird. Die Funktion setzt nämlich ein Nullbyte an die Stelle
des gefundenen Trennzeichens im *Original*-Puffer.
#417615
Lesenswert?

> :-) in zukunft werd ich mit static oder GLOBAL arbeiten!!!

Beides sind Varianten, die du nicht machen solltest!

DIe Methode: Einen Zeiger auf die Speicherfläche in die Funktion
hinein zu übergeben, ist die Methode, die du benutzen solltest.

/* Möglichkeit3: Einen Zeiger als Argument übergeben */
char *test4(char *ptr){
   strcpy(ptr, "testwert");
   return ptr;
}

Aber wenn du das schon so machst, dann falle nicht in die
gets() - fgets() Falle hinein:
Frage: Was ist falsch an gets() und wurde mit fgets() behoben?
Antwort: Man hat verabsäumt bei gets() einen weiteren Parameter
mit aufzunehmen, der gets darüber informiert wie gross den nun
der Buffer ist, in den es schreiben darf.

Genau wie bei test4()
test4 hat keine Möglichkeit festzustellen, wie gross den die Speicher-
fläche ist, auf die es einen Pointer bekommen hat. Daher kann test4
sich nicht davor schützen, diesen Buffer zu überlaufen.

Richtig wäre:

/* Möglichkeit3: Einen Zeiger als Argument übergeben */
char *test4(char *ptr, size_t BufferSize){
   if( BufferSize < 9 )
     return NULL;
   strcpy(buffer, "testwert");
   return ptr;
}

Und auf der Aufrufseite folgerichtig:

int main()
{
  char Buffer[4];

  if( test4( Buffer, sizeof( Buffer ) ) == NULL )
    printf( "Fehler: Buffer zu klein. Machen Sie das Array
groesser\n" );
}
Gast #417616
Lesenswert?

@Stefan am 25.09.2006 14:31

Ja, das sehe ich jetzt nach einem Blick in den K&R auch so. Dann wird
wohl nur der Zeiger auf den Original-Puffer statisch gespeichert, nicht
aber der Puffer selber in einen statischen Puffer kopiert.

Die Rückgabe des Zeigers über strtok() ist im hier diskutierten Fall
demnach also genauso sinnlos wie den lokal definierten String direkt
zurückzugeben.

Grüße

mplusplus
Beitrag #6958931 wurde von einem Moderator gelöscht.
Beitrag #6958947 wurde von einem Moderator gelöscht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren