Lendo uma string com scanf

147

Estou um pouco confuso sobre alguma coisa. Fiquei com a impressão de que a maneira correta de ler uma string C scanf()seguia as linhas de

(não importa o possível estouro de buffer, é apenas um exemplo simples)

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

No entanto, o seguinte parece funcionar também,

scanf( "%s" , &string );

Este é apenas o meu compilador (gcc), pura sorte ou algo mais?

abeln
fonte
No segundo caso, não há realmente possível estouro de buffer, pois você não está usando esse buffer. Ou você pode dizer que qualquer string maior que 3 caracteres excederá seu "buffer".
TED
Eu estava me referindo ao primeiro exemplo. Além disso, outros já apontaram o que está acontecendo aqui.
Abeln
Sim. Tentei, e Gareth está certo. Esquisito.
TED
Como essa pergunta é retornada pela pesquisa de "como ler uma string com scanf", pode ser apropriado salientar que esta pergunta é sobre maneiras de passar o ponteiro para o buffer scanfe a pergunta e a resposta aceita se concentram em e omita as restrições criticamente importantes para o comprimento máximo de entrada que devem ser usadas no código real (mas estão além do objetivo desta pergunta).
Arkku 21/09/18

Respostas:

141

Uma matriz "decai" em um ponteiro para seu primeiro elemento, portanto scanf("%s", string)é equivalente a scanf("%s", &string[0]). Por outro lado, scanf("%s", &string)passa um ponteiro para char[256], mas aponta para o mesmo local.

Então scanf, ao processar o final de sua lista de argumentos, tentará extrair a char *. Essa é a coisa certa quando você passa stringou &string[0], mas quando passa, &stringdepende de algo que o padrão de linguagem não garante, a saber, os ponteiros &stringe &string[0]- ponteiros para objetos de diferentes tipos e tamanhos que começar no mesmo lugar - são representados da mesma maneira.

Não acredito que já tenha encontrado um sistema no qual isso não funcione e, na prática, você provavelmente está seguro. No entanto, está errado e pode falhar em algumas plataformas. (Exemplo hipotético: uma implementação de "depuração" que inclui informações de tipo com todos os ponteiros. Acho que a implementação C na Symbolics "Lisp Machines" fez algo assim.)

Gareth McCaughan
fonte
5
+1. Também é trivial para verificar que os resultados da série de decaimento em &stringtrabalhar o mesmo que string(em vez de resultar em corrupção de memória aleatório, como outras respostas incorretamente assert):printf("%x\n%x\n", string, &string);
Josh Kelley
@ Josh Kelley interessante, eu diria que esse não seria o caso de um ponteiro para uma string alocada através do malloc ()?
Abeln
4
@abeln: Correto. Para uma sequência alocada através de malloc (), stringé um ponteiro e &stringé o endereço desse ponteiro, portanto os dois NÃO são intercambiáveis. Como Gareth explica, mesmo no caso de decaimento de array, stringe &stringtecnicamente não são os mesmos tipos (mesmo que sejam intercambiáveis ​​para a maioria das arquiteturas), e o gcc dará um aviso para o seu segundo exemplo se você ativar -Wall.
21711 Josh Kelley
Josh Kelley: obrigado, estou feliz por poder esclarecer isso.
Abeln
1
@JCooper str, & str [0] representam a mesma coisa (o início da matriz), e embora não necessariamente o & str também seja o mesmo endereço de memória. Agora strPtr aponta para str, então o endereço de memória armazenado dentro do strPtr é o mesmo que str e isso é 12fe60. Finalmente & strPtr é o endereço da variável strPtr, este não é o valor armazenado em strptr, mas o endereço de memória real de strPtr. Desde StrPtr é uma variável diferente de todos os outros, ele também tem um endereço de memória diferente, neste caso 12fe54
Juan Besa
-9

Eu acho que isso abaixo é preciso e pode ajudar. Sinta-se livre para corrigi-lo se encontrar algum erro. Eu sou novo em C.

char str[]  
  1. matriz de valores do tipo char, com seu próprio endereço na memória
  2. matriz de valores do tipo char, com seu próprio endereço na memória, tantos endereços consecutivos quanto elementos na matriz
  3. incluindo caractere nulo de terminação '\0' &str, &str[0]e str, todos os três representam o mesmo local na memória que é o endereço do primeiro elemento da matrizstr

    char * strPtr = & str [0]; // declaração e inicialização

Como alternativa, você pode dividir isso em dois:

char *strPtr; strPtr = &str[0];
  1. strPtr é um ponteiro para um char
  2. strPtr pontos na matriz str
  3. strPtr é uma variável com seu próprio endereço na memória
  4. strPtr é uma variável que armazena o valor do endereço &str[0]
  5. strPtr próprio endereço na memória é diferente do endereço de memória que ele armazena (endereço do array na memória, também conhecido como & str [0])
  6. &strPtr representa o endereço do próprio strPtr

Eu acho que você poderia declarar um ponteiro para um ponteiro como:

char **vPtr = &strPtr;  

declara e inicializa com o endereço do ponteiro strPtr

Como alternativa, você pode dividir em dois:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr pontos no ponteiro strPtr
  2. *vPtr é uma variável com seu próprio endereço na memória
  3. *vPtr é uma variável que armazena o valor do endereço & strPtr
  4. comentário final: você não pode fazer str++, o strendereço é um const, mas você pode fazerstrPtr++
angelita
fonte