Pregunta

considere el programa a continuación

    char str[5];
    strcpy(str,"Hello12345678");
    printf("%s",str);

Cuando se ejecuta este programa, se produce un error de segmentación.

Pero cuando se reemplaza strcpy con lo siguiente, el programa funciona bien.

strcpy(str,"Hello1234567");

Entonces, la pregunta es que debería fallar al intentar copiar a cadena cualquier otra cadena de más de 5 caracteres.

Entonces, ¿por qué no falla para "Hello1234567" y solo falla para "Hello12345678", es decir, para una cadena con una longitud de 13 o más de 13?

Este programa se ejecutó en una máquina de 32 bits.

¿Fue útil?

Solución

Hay tres tipos de comportamiento de las normas que debe interesarle.

1 / comportamiento definido . Esto funcionará en todas las implementaciones que cumplen. Utilice esta libremente.

2 / comportamiento definido por la implementación . Como se ha dicho, que depende de la implementación, pero al menos todavía está definido. Se requieren implementaciones para documentar lo que hacen en estos casos. Utilice esta opción si no se preocupan por la portabilidad.

3 / Indefinido comportamiento . Cualquier cosa puede suceder. Y nos referimos a lo , hasta e incluyendo todo el equipo colapsar en una singularidad desnuda y la deglución en sí, y una gran parte de sus compañeros de trabajo. Nunca utilice este. ¡Siempre! ¡Seriamente! No me hagas ir por allí.

Copia de más de 4 caracteres y un cero bytes a un char[5] es un comportamiento indefinido.

En serio, no importa qué su programa se bloquea con 14 caracteres, pero no 13, que está casi seguro de sobrescribir alguna información no estrellarse en la pila y su programa lo más probable es producir resultados incorrectos de todos modos. De hecho, el accidente es mejor por lo menos desde que se detenga que depender de los efectos posiblemente malos.

Aumentar el tamaño de la matriz a algo más adecuado (char[14] en este caso con la información disponible) o usar alguna otra estructura de datos que puede hacer frente.


Actualización:

Ya que parece tan preocupado por averiguar por qué un extra de 7 caracteres no causa problemas, pero no 8 caracteres, vamos a prever la posible disposición de pila al entrar en main(). Digo "posible" ya que la disposición real depende de la convención de llamada que utiliza el compilador. Dado que el código de inicio de C llama main() con argc y argv, la pila en el inicio de main(), después de asignar espacio para un char[5], podría tener este aspecto:

+------------------------------------+
| C start-up code return address (4) |
| argc (4)                           |
| argv (4)                           |
| x = char[5] (5)                    |
+------------------------------------+

Cuando se escribe la Hello1234567\0 bytes con:

strcpy (x, "Hello1234567");

para x, sobrescribe el argc y argv pero, al regresar de main(), eso está bien. Específicamente Hello rellena x, 1234 rellena argv y 567\0 rellena argc. Siempre y cuando en realidad no se trata de utilizar argc y / o argv después de eso, va a estar bien:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |
| argc (4)                           |   '567<NUL>'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

Sin embargo, si se escribe Hello12345678\0 (tenga en cuenta el extra "8") a x, sobrescribe el argc y argv y un byte de la dirección de retorno de manera que, cuando main() intenta volver a el código de inicio de C, y sale en tierra de hadas en su lugar:

+------------------------------------+ Overwrites with:
| C start-up code return address (4) |   '<NUL>'
| argc (4)                           |   '5678'
| argv (4)                           |   '1234'
| x = char[5] (5)                    |   'Hello'
+------------------------------------+

Una vez más, esto depende enteramente de la convención de llamada de su compilador. Es posible que un compilador diferente sería ALWAYS cabo matrices a un múltiplo de 4 bytes y el código no fallaría allí hasta que anotó otros tres caracteres. Incluso el mismo compilador puede asignar las variables en el marco de pila diferente para asegurar la alineación está satisfecho.

Eso es lo que quieren decir con indefinido: No saben lo que va a suceder

.

Otros consejos

Estás copiando a la pila, por lo que depende de lo que el compilador haya colocado en la pila, la cantidad de datos adicionales que se necesitarán para bloquear tu programa.

Algunos compiladores pueden producir código que fallará con solo un byte sobre el tamaño del búfer; no está definido cuál es el comportamiento.

Supongo que el tamaño 13 es suficiente para sobrescribir la dirección del remitente, o algo similar, que falla cuando regresa la función.Pero otro compilador u otra plataforma podría fallar con una longitud diferente.

Además, su programa podría fallar con una duración diferente si se ejecutó durante más tiempo, si se sobrescribiera algo menos importante.

Para la plataforma Intel de 32 bits la explicación es la siguiente. Cuando se declara char [5] en la pila el compilador asigna realmente 8 bytes debido a la alineación. Entonces es típico de las funciones que tengan la siguiente prólogo:

push ebp
mov ebp, esp

esto ahorra valor del registro ebp en la pila, luego se mueve esp valor del registro en ebp para el uso de esp valor para acceder a los parámetros. Esto conduce a más de 4 bytes en la pila para ser ocupados con valor ebp.

En el epílogo ebp se restaura, pero su valor es por lo general sólo se utiliza para acceder a los parámetros de la función de pila asignados, por lo que puede no sobreescribiéndolo herido en la mayoría de los casos.

Por lo que tiene la siguiente distribución (pila crece hacia abajo en Intel.): 8 bytes para la matriz, luego 4 bytes para ebp, entonces por lo general la dirección de retorno

Esta es la razón por lo que necesita para sobrescribir al menos 13 bytes a bloquear el programa.

Para añadir a las respuestas anteriores: se puede probar para los insectos como estos con una herramienta como Valgrind . Si estás en Windows, echar un vistazo a este hilo para .

Depende de lo que hay en la pila después de la matriz "str". Usted acaba de pasar a no ser pisoteando nada crítico hasta que se copia que muchos caracteres.

Por lo tanto, va a depender de lo demás está en la función, el compilador que utiliza y, posiblemente, las opciones del compilador también.

13 es 5 + 8, lo que sugiere que hay dos palabras no críticas después de la matriz str, entonces algo crítico (tal vez la dirección de retorno)

Esa es la belleza pura de un comportamiento indefinido (UB): esto es indefinido

.

Su código:

char str[5];
strcpy(str,"Hello12345678");

Escribe 14 bytes / caracteres a str que sólo puede contener 5 bytes / caracteres. Esto invoca UB.

Q:. Así que por qué no se bloquea para "Hello1234567" y sólo estrellarse para "Hello12345678" es decir, con una longitud de cadena de 13 o más de 13

  

Debido a que el comportamiento no está definido.   Utilice strncpy. Ver esta página    http://en.wikipedia.org/wiki/Strcpy   para obtener más información.

strncpy no es seguro ya que no añade una terminación NULL si la cadena de origen tiene una longitud> = n donde n es el tamaño de la cadena de destino.

char s[5];
strncpy(s,5,"test12345");
printf("%s",s); // crash

Siempre usamos strlcpy para aliviar esto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top