Leggere una stringa con scanf


147

Sono un po 'confuso per qualcosa. Ho avuto l'impressione che il modo corretto di leggere una stringa C con scanf()andato lungo le linee di

(non importa il possibile overflow del buffer, è solo un semplice esempio)

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

Tuttavia, anche il seguente sembra funzionare,

scanf( "%s" , &string );

È solo il mio compilatore (gcc), pura fortuna o qualcos'altro?


Nel secondo caso, in realtà non esiste un possibile overflow del buffer, poiché non si utilizza affatto quel buffer. O quello, o potresti dire che qualsiasi stringa più grande di 3 caratteri traboccerà il tuo "buffer".
TED

Mi riferivo al primo esempio. Inoltre, altri hanno già sottolineato cosa sta succedendo qui.
abeln,

Sì. L'ho provato e Gareth ha ragione. Strano.
TED

Poiché questa domanda viene restituita dalle ricerche di "come leggere una stringa con scanf", potrebbe essere opportuno sottolineare che questa domanda riguarda i modi in cui passare il puntatore al buffer scanfe sia la domanda che la risposta accettata si concentrano su quello e omettere le restrizioni di fondamentale importanza per la massima lunghezza di input che dovrebbero essere usate nel codice reale (ma oltre al punto per questa domanda).
Arkku,

Risposte:


141

Una matrice "decade" in un puntatore al suo primo elemento, quindi scanf("%s", string)equivale a scanf("%s", &string[0]). D'altra parte, scanf("%s", &string)passa un puntatore a char[256], ma punta allo stesso posto.

Quindi scanf, durante l'elaborazione della coda del suo elenco di argomenti, tenterà di estrarre a char *. Questa è la cosa giusta quando sei passato stringo &string[0], ma quando sei passato &stringdipende da qualcosa che lo standard linguistico non garantisce, vale a dire che i puntatori &stringe &string[0]- puntatori a oggetti di diversi tipi e dimensioni che iniziare nello stesso posto - sono rappresentati allo stesso modo.

Non credo di aver mai incontrato un sistema su cui non funziona, e in pratica probabilmente sei al sicuro. Tuttavia, è sbagliato e potrebbe non funzionare su alcune piattaforme. (Esempio ipotetico: un'implementazione di "debug" che include informazioni sul tipo con ogni puntatore. Penso che l'implementazione C sulle "Macchine Lisp" di Symbolics abbia fatto qualcosa del genere.)


5
+1. È anche banale verificare che il decadimento dell'array &stringfunzioni nello stesso modo string(invece di provocare un danneggiamento della memoria casuale, come affermano erroneamente altre risposte):printf("%x\n%x\n", string, &string);
Josh Kelley

@Josh Kelley interessante, lo prenderei allora che questo non sarebbe il caso di un puntatore a una stringa allocata tramite malloc ()?
circa

4
@abeln: corretto. Per una stringa allocata tramite malloc (), stringè un puntatore ed &stringè l'indirizzo di quel puntatore, quindi i due NON sono intercambiabili. Come spiega Gareth, anche nel caso del decadimento dell'array, stringe &stringtecnicamente non sono gli stessi tipi (anche se capita di essere intercambiabili per la maggior parte delle architetture), e gcc fornirà un avviso per il tuo secondo esempio se si accende -Wall.
Josh Kelley,

@Josh Kelley: grazie, sono felice di avermi chiarito la mente.
abeln,

1
@JCooper str, & str [0] rappresentano entrambi la stessa cosa (l'inizio dell'array), e sebbene non necessariamente lo & str sia anche lo stesso indirizzo di memoria. Ora strPtr punta a str, quindi l'indirizzo di memoria memorizzato all'interno di strPtr è lo stesso di str e questo è 12fe60. Infine & strPtr è l'indirizzo della variabile strPtr, questo non è il valore memorizzato in strptr ma l'indirizzo di memoria effettivo di strPtr. Poiché strptr è una variabile diversa rispetto a tutte le altre, ha anche un indirizzo di memoria diverso, in questo caso 12fe54
Juan Besa,

-9

Penso che questo di seguito sia accurato e possa essere d'aiuto. Sentiti libero di correggerlo se trovi errori. Sono nuovo in C.

char str[]  
  1. matrice di valori di tipo char, con il proprio indirizzo in memoria
  2. matrice di valori di tipo char, con il proprio indirizzo in memoria tanti indirizzi consecutivi quanti elementi nella matrice
  3. compresi terminazione carattere nullo '\0' &str, &str[0]e strtutti e tre rappresentano la stessa posizione nella memoria che è l'indirizzo del primo elemento dell'arraystr

    char * strPtr = & str [0]; // dichiarazione e inizializzazione

in alternativa, puoi dividerlo in due:

char *strPtr; strPtr = &str[0];
  1. strPtr è un puntatore a char
  2. strPtr punti nella matrice str
  3. strPtr è una variabile con il proprio indirizzo in memoria
  4. strPtr è una variabile che memorizza il valore dell'indirizzo &str[0]
  5. strPtr il proprio indirizzo in memoria è diverso dall'indirizzo di memoria che memorizza (indirizzo dell'array in memoria aka & str [0])
  6. &strPtr rappresenta l'indirizzo di strPtr stesso

Penso che potresti dichiarare un puntatore a un puntatore come:

char **vPtr = &strPtr;  

dichiara e inizializza con l'indirizzo del puntatore strPtr

In alternativa puoi dividere in due:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr punta al puntatore strPtr
  2. *vPtr è una variabile con il proprio indirizzo in memoria
  3. *vPtr è una variabile che memorizza il valore di address & strPtr
  4. commento finale: non puoi farlo str++, l' strindirizzo è a const, ma puoi farlostrPtr++
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.