Declarar cadenas de longitud variable en c – ¿aparentemente dinámicas?

Estoy tratando de entender algo básico acerca de cómo se definen las “cadenas” en c.

char s[2]; scanf("%s", s); printf("%s", s); printf("sizeof s %d", sizeof(s)); 

No soy progtwigdor de CA Soy consciente de que hay problemas con el uso de scanf para obtener comentarios del usuario y que ni siquiera estoy comprobando su valor de retorno, etc. Esto es solo para entender algo básico sobre la statement de cadenas.

Dado el código anterior, lo que hace si ingreso dice “helloworld” se imprime “helloworld”. Bueno. Pero pensé que al decir char s [2] estaba diciendo que algo como ‘s es una serie de longitud dos donde cada elemento es de tipo char’.

Por lo tanto, esperaba ver “él” impreso. No ‘helloworld’. Porque mi matriz solo tiene espacio para 2 caracteres.

El tamaño de aún devuelve 2. Pero parece que mi matriz ha crecido hasta el tamaño de la entrada del usuario.

¿Que esta pasando?

Dos cuestiones:

  • C no realiza ninguna comprobación de límites en los accesos de matriz;
  • En la mayoría de los casos, una expresión de tipo de matriz se convertirá en una expresión de tipo de puntero, y el valor de la expresión será la dirección del primer elemento de la matriz. Esto incluye expresiones matriciales pasadas como argumentos a funciones.

La especificación de conversión %s le dice a printf que imprima la secuencia de caracteres comenzando en la dirección especificada hasta que vea el terminador 0 al final de la cadena. De manera similar, le dice a scanf que almacene una secuencia de caracteres que comienza en la dirección especificada hasta que vea un carácter de espacio en blanco o EOF.

Cuando pasa la expresión s como un argumento a scanf o printf , la expresión se convierte de tipo “matriz de 2 elementos de char ” a “puntero a char “, y el valor de la expresión es la dirección del primer elemento de la array (es equivalente a pasar la expresión &s[0] ).

All scanf recibe es un valor de puntero, no tiene idea de qué tan grande es la matriz que comienza en esa dirección. Por lo tanto, no sabe que s solo es lo suficientemente grande como para contener dos caracteres. En su lugar, felizmente escribe esos caracteres adicionales más allá del final de la matriz. De manera similar, printf no sabe que la matriz solo tiene 2 caracteres de ancho; solo sigue imprimiendo hasta que ve el terminador 0.

Puede especificar un ancho de campo como parte de la conversión:

 scanf( "%1s", s ); 

Esto leerá a lo sumo 1 carácter de la entrada estándar y lo almacenará en s . Recuerde que una cadena es una secuencia de caracteres seguida de un terminador 0, por lo que para almacenar una cadena de N caracteres, debe reservar una matriz de N + 1 elementos para almacenarla.

Esto muestra la razón perfecta de por qué scanf es tan peligroso. Has sobrescrito otra memoria que no estaba destinada a esto. Una forma mucho más segura de leer cadenas de longitud constante es hacer algo como esto

 char a[2]; fgets(a, sizeof(a), stdin); printf("%s\n", a); 

Si hizo esto y escribió helloworld , solo obtendría h en stdout (porque las cadenas tienen un sizeof(a) - 1 caracteres debido al terminador nulo. Esto significa que la matriz a es en realidad {'h', '\0'} ) . fgets es mucho más seguro para leer cadenas de tamaño constante que scanf .

No creo que nadie haya respondido esta pregunta todavía. Hay dos problemas aquí…

Primero, la matriz “s” tiene solo 2 bytes de longitud, por lo que scanf de cualquier cadena de más de 1 carácter generará resultados no deseados.

 char s[255]; // was char s[2]; 

Además, como se mencionó anteriormente, scanf es una forma horrible de ingresar cadenas. Use fgets en su lugar.

 fgets(s, sizeof(s), stdin); // was scanf("%s", s); 

Segundo, no puedes usar sizeof para obtener la longitud de una cadena. Le devolverá la longitud de la matriz que contiene la cadena (no lo que quiere, confíe en mí). En su lugar, utilice strlen .

 printf("strlen s %d", strlen(s)); // was printf("sizeof s %d", sizeof(s));