Leer una cadena con scanf

147

Estoy un poco confundido acerca de algo. Tenía la impresión de que la forma correcta de leer una cadena C scanf()seguía las líneas de

(No importa el posible desbordamiento del búfer, es solo un ejemplo simple)

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

Sin embargo, lo siguiente parece funcionar también,

scanf( "%s" , &string );

¿Es solo mi compilador (gcc), pura suerte o algo más?

abeln
fuente
En el segundo caso, en realidad no hay un posible desbordamiento del búfer, ya que no está utilizando ese búfer en absoluto. O eso, o podría decir que cualquier cadena de más de 3 caracteres desbordará su "búfer".
TED
Me refería al primer ejemplo. Además, otros ya han señalado lo que está sucediendo aquí.
Abeln
Sip. Lo probé y Gareth tiene razón. Extraño.
TED
Dado que esta pregunta es devuelta por las búsquedas de "cómo leer una cadena con scanf", podría ser apropiado señalar que esta pregunta trata sobre formas de pasar el puntero al búfer scanf, y tanto la pregunta como la respuesta aceptada se centran en eso, y omita las restricciones críticamente importantes para la longitud máxima de entrada que deberían usarse en el código real (pero que están fuera del punto de esta pregunta).
Arkku

Respuestas:

141

Una matriz "decae" en un puntero a su primer elemento, por lo que scanf("%s", string)es equivalente a scanf("%s", &string[0]). Por otro lado, scanf("%s", &string)pasa un puntero a char[256], pero apunta al mismo lugar.

Luego scanf, al procesar la cola de su lista de argumentos, intentará extraer a char *. Eso es lo correcto cuando ha pasado stringo &string[0], pero cuando ha pasado &stringdepende de algo que el estándar del lenguaje no garantiza, a saber, los punteros &stringy los &string[0]punteros a objetos de diferentes tipos y tamaños que comenzar en el mismo lugar: se representan de la misma manera.

No creo haber encontrado un sistema en el que eso no funcione, y en la práctica probablemente estés a salvo. Sin embargo, está mal y podría fallar en algunas plataformas. (Ejemplo hipotético: una implementación de "depuración" que incluye información de tipo con cada puntero. Creo que la implementación de C en las "Máquinas Lisp" de Symbolics hizo algo como esto).

Gareth McCaughan
fuente
55
+1. También es trivial verificar que la descomposición de la matriz &stringfuncione de la misma manera que string(en lugar de provocar daños aleatorios en la memoria, como afirman incorrectamente otras respuestas):printf("%x\n%x\n", string, &string);
Josh Kelley
@Josh Kelley interesante, supongo que este no sería el caso con un puntero a una cadena asignada a través de malloc ()?
Abeln
44
@abeln: Correcto. Para una cadena asignada a través de malloc (), stringes un puntero y &stringes la dirección de ese puntero, por lo que los dos NO son intercambiables. Como explica Gareth, incluso en el caso de la descomposición de la matriz, stringy &stringtécnicamente no son del mismo tipo (aunque sean intercambiables para la mayoría de las arquitecturas), y gcc le dará una advertencia para su segundo ejemplo si lo activa -Wall.
Josh Kelley
@ Josh Kelley: gracias, me alegra haber podido aclarar mi mente sobre esto.
Abeln
1
@JCooper str, & str [0] ambos representan la misma cosa (el inicio de la matriz), y aunque no necesariamente, & str también es la misma dirección de memoria. Ahora strPtr apunta a str para que la dirección de memoria almacenada dentro de strPtr sea la misma que str y eso es 12fe60. Finalmente & strPtr es la dirección de la variable strPtr, este no es el valor almacenado en strptr sino la dirección de memoria real de strPtr. Dado que strptr es una variable diferente a todas las demás, también tiene una dirección de memoria diferente, en este caso 12fe54
Juan Besa
-9

Creo que esto a continuación es exacto y puede ayudar. Siéntase libre de corregirlo si encuentra algún error. Soy nuevo en C.

char str[]  
  1. conjunto de valores de tipo char, con su propia dirección en memoria
  2. matriz de valores de tipo char, con su propia dirección en la memoria tantas direcciones consecutivas como elementos en la matriz
  3. incluyendo el carácter nulo de terminación '\0' &str, &str[0]y str, los tres representan la misma ubicación en la memoria, que es la dirección del primer elemento de la matrizstr

    char * strPtr = & str [0]; // declaración e inicialización

Alternativamente, puede dividir esto en dos:

char *strPtr; strPtr = &str[0];
  1. strPtr es un puntero a un char
  2. strPtr puntos en la matriz str
  3. strPtr es una variable con su propia dirección en la memoria
  4. strPtr es una variable que almacena el valor de la dirección &str[0]
  5. strPtr La propia dirección en la memoria es diferente de la dirección de memoria que almacena (dirección de la matriz en la memoria aka & str [0])
  6. &strPtr representa la dirección de strPtr en sí

Creo que podría declarar un puntero a un puntero como:

char **vPtr = &strPtr;  

declara e inicializa con la dirección del puntero strPtr

Alternativamente, podría dividirse en dos:

char **vPtr;
*vPtr = &strPtr
  1. *vPtr apunta al puntero strPtr
  2. *vPtr es una variable con su propia dirección en la memoria
  3. *vPtr es una variable que almacena el valor de dirección y strPtr
  4. Comentario final: no se puede hacer str++, la strdirección es una const, pero se puede hacerstrPtr++
angelita
fuente