Lesen eines Strings mit scanf

147

Ich bin ein bisschen verwirrt über etwas. Ich hatte den Eindruck, dass die richtige Art, eine C-Saite mit zu lesen, scanf()nach dem Vorbild von

(egal der mögliche Pufferüberlauf, es ist nur ein einfaches Beispiel)

char string[256];
scanf( "%s" , string );

Das Folgende scheint jedoch auch zu funktionieren:

scanf( "%s" , &string );

Ist das nur mein Compiler (gcc), reines Glück oder etwas anderes?

abeln
quelle
Im zweiten Fall ist tatsächlich kein Pufferüberlauf möglich, da Sie diesen Puffer überhaupt nicht verwenden. Entweder das, oder Sie könnten sagen, dass jede Zeichenfolge, die größer als 3 Zeichen ist, Ihren "Puffer" überläuft.
TED
Ich bezog mich auf das erste Beispiel. Auch andere haben bereits darauf hingewiesen, was hier los ist.
Abeln
Jep. Versuchte es und Gareth hat recht. Seltsam.
Ted
Da diese Frage durch die Suche nach "Lesen einer Zeichenfolge mit scanf" zurückgegeben wird, sollte darauf hingewiesen werden, dass es bei dieser Frage um Möglichkeiten geht, den Zeiger an den Puffer zu übergeben scanf, und dass sich sowohl die Frage als auch die akzeptierte Antwort darauf konzentrieren das, und lassen Sie die kritisch wichtigen Einschränkungen für die maximale Eingabelänge weg, die in echtem Code verwendet werden sollten (sind aber neben dem Punkt für diese Frage).
Arkku

Antworten:

141

Ein Array "zerfällt" in einen Zeiger auf sein erstes Element, scanf("%s", string)entspricht also scanf("%s", &string[0]). Auf der anderen Seite wird scanf("%s", &string)ein Zeiger auf- übergeben char[256], der jedoch auf dieselbe Stelle zeigt.

Dann scanf, wenn das Heck seines Arguments Listenverarbeitung wird versuchen , ein zu ziehen char *. Das ist das Richtige, wenn Sie übergeben haben stringoder &string[0], aber wenn Sie übergeben haben, hängen &stringSie von etwas ab, das der Sprachstandard nicht garantiert, nämlich dass die Zeiger &stringund &string[0]- Zeiger auf Objekte unterschiedlicher Typen und Größen, die am selben Ort beginnen - werden auf die gleiche Weise dargestellt.

Ich glaube nicht, dass ich jemals auf ein System gestoßen bin, auf dem das nicht funktioniert, und in der Praxis sind Sie wahrscheinlich sicher. Trotzdem ist es falsch und es könnte auf einigen Plattformen fehlschlagen. (Hypothetisches Beispiel: Eine "Debugging" -Implementierung, die Typinformationen zu jedem Zeiger enthält. Ich denke, die C-Implementierung auf den Symbolics "Lisp Machines" hat so etwas getan.)

Gareth McCaughan
quelle
5
+1. Es ist auch trivial zu überprüfen, ob der Array-Zerfall &stringgenauso funktioniert wie string(anstatt zu einer zufälligen Speicherbeschädigung zu führen, wie andere Antworten fälschlicherweise behaupten):printf("%x\n%x\n", string, &string);
Josh Kelley
@ Josh Kelley interessant, würde ich dann annehmen, dass dies bei einem Zeiger auf eine durch malloc () zugewiesene Zeichenfolge nicht der Fall wäre?
Abeln
4
@abeln: Richtig. Für eine über malloc () zugewiesene Zeichenfolge stringhandelt es sich um einen Zeiger und &stringdie Adresse dieses Zeigers, sodass die beiden NICHT austauschbar sind. Wie Gareth erklärt, auch für den Fall der Anordnung Verfall, stringund &stringsind technisch nicht die gleichen Typen (auch wenn sie für die meisten Architekturen austauschbar passieren) und gcc eine Warnung für Ihr zweites Beispiel geben , wenn Sie einschalten -Wall.
Josh Kelley
@ Josh Kelley: Danke, ich bin froh, dass ich mich darüber klar werden konnte.
Abeln
1
@JCooper str, & str [0] repräsentieren beide dasselbe (den Anfang des Arrays), und obwohl nicht unbedingt, ist & str auch dieselbe Speicheradresse. Jetzt zeigt strPtr auf str, sodass die in strPtr gespeicherte Speicheradresse mit str identisch ist und das ist 12fe60. Schließlich ist & strPtr die Adresse der Variablen strPtr. Dies sind nicht die in strptr gespeicherten Werte, sondern die tatsächliche Speicheradresse von strPtr. Da strptr eine andere Variable als alle anderen ist, hat es auch eine andere Speicheradresse, in diesem Fall 12fe54
Juan Besa
-9

Ich denke, dass dies unten korrekt ist und es helfen kann. Fühlen Sie sich frei, es zu korrigieren, wenn Sie Fehler finden. Ich bin neu bei C.

char str[]  
  1. Array von Werten vom Typ char mit eigener Adresse im Speicher
  2. Array von Werten vom Typ char, mit einer eigenen Adresse im Speicher, so viele aufeinanderfolgende Adressen wie Elemente im Array
  3. einschließlich der Beendigung Nullzeichen '\0' &str, &str[0]und stralle drei repräsentieren den gleichen Speicherort, der die Adresse des ersten Elements des Arrays iststr

    char * strPtr = & str [0]; // Deklaration und Initialisierung

Alternativ können Sie dies in zwei Teile teilen:

char *strPtr; strPtr = &str[0];
  1. strPtr ist ein Zeiger auf a char
  2. strPtr Punkte auf Array str
  3. strPtr ist eine Variable mit einer eigenen Adresse im Speicher
  4. strPtr ist eine Variable, die den Adresswert speichert &str[0]
  5. strPtr Die eigene Adresse im Speicher unterscheidet sich von der Speicheradresse, die sie speichert (Adresse des Arrays im Speicher aka & str [0]).
  6. &strPtr repräsentiert die Adresse von strPtr selbst

Ich denke, dass Sie einen Zeiger auf einen Zeiger deklarieren könnten als:

char **vPtr = &strPtr;  

deklariert und initialisiert mit der Adresse des strPtr-Zeigers

Alternativ können Sie sich in zwei Teile teilen:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr zeigt auf den strPtr-Zeiger
  2. *vPtr ist eine Variable mit einer eigenen Adresse im Speicher
  3. *vPtr ist eine Variable, die den Wert von address & strPtr speichert
  4. letzter Kommentar: Sie können nicht tun str++, strAdresse ist eine const, aber Sie können tunstrPtr++
Angelita
quelle