Estaba investigando un poco sobre un tema cuando me encontré con esta situación. Suponga el siguiente código C:
#include <stdio.h> int main() { char name[1]; scanf("%s",name); printf("Hi %s",name); return 0; } Compilé con -fno-stack-protector y lo probé con una entrada de más de 1, como John , y para mi sorpresa, ¡funciona!
¿No debería arrojar una falla de segmentación cuando la entrada es más larga que 1?
Eventualmente se rompió con Alexander como entrada (9) pero funciona con menos de 9.
¿Por qué funciona con entradas más largas que la longitud de la matriz de nombres?
PD: Estoy usando Ubuntu (64 bits), gcc versión 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04) y CLion como IDE.
Este es un comportamiento indefinido. Su programa tiene un desbordamiento de búfer, porque asigna exactamente un carácter, que es suficiente para almacenar una cadena vacía terminada en nulo.
Sin embargo, hay memoria adyacente a su búfer que no ha sido asignada a su programa. scanf coloca su entrada en esa memoria, porque no sabe cuánto tiempo es su búfer de cadena. Este es un gran peligro y una fuente de innumerables ataques de piratas informáticos, cuando se coloca una secuencia predeterminada de bytes en su cadena, con la esperanza de anular algunos elementos vitales y, finalmente, obtener el control.
Es por eso que usar %s sin especificar el tamaño es peligroso. Siempre debe agregar un límite de tamaño adecuado a %s , de lo contrario, su programa corre el riesgo de desbordarse el búfer.
char name[120]; scanf("%119s",name); Este programa es seguro, porque incluso si un usuario malintencionado escribe más de 120 caracteres, scanf ignoraría todo lo que supere el carácter 119, como se especifica en el formato %119s .
El tamaño y tipo de la variable donde almacena la entrada no tiene nada que ver con scanf .
scanf solo recibe una dirección (puntero) donde depositar la entrada que recibe del usuario.
Los compiladores inteligentes ahora le advierten si la cadena de formato pasada a scanf no coincide con el tipo de los parámetros, pero en principio, incluso podría declarar el name como un número entero:
int name; y mantendría la cadena de entrada bastante bien, hasta tres caracteres (el cuarto es para el final de la cadena, es decir, cero), asumiendo que el tamaño de int es de 32 bits, es decir, 4 bytes
El hecho de que funcione es pura mala suerte, ya que los datos de entrada, cuando son almacenados por scanf , se ejecutan más allá del final del búfer asignado para ellos ( name ).
Nota: asignar solo un carácter para una cadena nunca funcionaría, incluso para cadenas de entrada de un solo carácter. Siempre debe tener en cuenta el EOS que se utiliza para terminarlos. Por lo tanto, el name debe declararse como char name[2]; por lo menos.