Según esta película (alrededor del minuto 38), si tengo dos funciones con las mismas vars locales, usarán el mismo espacio. Entonces, el siguiente programa debería imprimirse 5. Compilándolo con gccresultados -1218960859. ¿por qué?
El programa:
#include <stdio.h>
void A()
{
int a;
printf("%i",a);
}
void B()
{
int a;
a = 5;
}
int main()
{
B();
A();
return 0;
}
según lo solicitado, aquí está la salida del desensamblador:
0804840c <A>:
804840c: 55 push ebp
804840d: 89 e5 mov ebp,esp
804840f: 83 ec 28 sub esp,0x28
8048412: 8b 45 f4 mov eax,DWORD PTR [ebp-0xc]
8048415: 89 44 24 04 mov DWORD PTR [esp+0x4],eax
8048419: c7 04 24 e8 84 04 08 mov DWORD PTR [esp],0x80484e8
8048420: e8 cb fe ff ff call 80482f0 <printf@plt>
8048425: c9 leave
8048426: c3 ret
08048427 <B>:
8048427: 55 push ebp
8048428: 89 e5 mov ebp,esp
804842a: 83 ec 10 sub esp,0x10
804842d: c7 45 fc 05 00 00 00 mov DWORD PTR [ebp-0x4],0x5
8048434: c9 leave
8048435: c3 ret
08048436 <main>:
8048436: 55 push ebp
8048437: 89 e5 mov ebp,esp
8048439: 83 e4 f0 and esp,0xfffffff0
804843c: e8 e6 ff ff ff call 8048427 <B>
8048441: e8 c6 ff ff ff call 804840c <A>
8048446: b8 00 00 00 00 mov eax,0x0
804844b: c9 leave
804844c: c3 ret
804844d: 66 90 xchg ax,ax
804844f: 90 nop
c
gcc
memory-management
elyashiv
fuente
fuente

Respuestas:
Sí, sí, este es un comportamiento indefinido , porque está utilizando la variable 1 sin inicializar .
Sin embargo, en la arquitectura x86 2 , este experimento debería funcionar . El valor no se "borra" de la pila, y dado que no está inicializado
B(), ese mismo valor debería estar allí, siempre que los marcos de la pila sean idénticos.Me atrevería a adivinar que, dado
int aque no se usa dentro devoid B(), el compilador optimizó ese código y nunca se escribió un 5 en esa ubicación en la pila. Trate de añadir unaprintfenB()así - sólo se puede trabajar.Además, los indicadores del compilador, es decir, el nivel de optimización, probablemente también afectarán este experimento. Intente deshabilitar las optimizaciones pasando
-O0a gcc.Editar: Acabo de compilar su código con
gcc -O0(64 bits) y, de hecho, el programa imprime 5, como esperaría alguien familiarizado con la pila de llamadas. De hecho, funcionó incluso sin él-O0. Una compilación de 32 bits puede comportarse de manera diferente.Descargo de responsabilidad: ¡Nunca, nunca uses algo como esto en código "real"!
1 - Hay un debate a continuación sobre si esto es oficialmente "UB" o simplemente impredecible.
2 - También x64, y probablemente cualquier otra arquitectura que use una pila de llamadas (al menos las que tengan una MMU)
Echemos un vistazo a la razón por la que no funcionó. Esto se ve mejor en 32 bits, por lo que compilaré con
-m32.Compilé con
$ gcc -m32 -O0 test.c(Optimizaciones deshabilitadas). Cuando ejecuto esto, imprime basura.Mirando
$ objdump -Mintel -d ./a.out:Vemos que en
B, el compilador reservó 0x10 bytes de espacio de pila e inicializó nuestraint avariable en[ebp-0x4]5.En
ASin embargo, el compilador colocaint aal[ebp-0xc]. Entonces, en este caso, nuestras variables locales no terminaron en el mismo lugar. Al agregar unaprintf()llamadaAtambién, los marcos de pila paraAyBserán idénticos y se imprimirán55.fuente
Es un comportamiento indefinido . Una variable local no inicializada tiene un valor indeterminado y su uso dará lugar a un comportamiento indefinido.
fuente
{ int uninit; &uninit; printf("%d\n", uninit); }Aún tiene un comportamiento indefinido. Por otro lado, puede tratar cualquier objeto como una matriz deunsigned char; ¿Eso es lo que tenías en mente?Una cosa importante para recordar: ¡ nunca confíe en algo así y nunca lo use en código real! Es solo algo interesante (que incluso no siempre es cierto), no una característica o algo así. Imagínese tratando de encontrar un error producido por ese tipo de "característica": pesadilla.
Por cierto. - C y C ++ están llenos de ese tipo de "características", aquí hay una GRAN presentación de diapositivas al respecto: http://www.slideshare.net/olvemaudal/deep-c Entonces, si desea ver más "características" similares, comprenda qué bajo el capó y cómo está funcionando, solo mire esta presentación de diapositivas; no se arrepentirá y estoy seguro de que incluso la mayoría de los programadores experimentados de c / c ++ pueden aprender mucho de esto.
fuente
En la función
A, la variableano se inicializa, imprimir su valor conduce a un comportamiento indefinido.En algunos compiladores, la variable
ainAyainBestán en la misma dirección, por lo que puede imprimirse5, pero nuevamente, no se puede confiar en un comportamiento indefinido.fuente
s machine will be the same depends on the assembly generated by the compiler. As @JonathonReinhart pointed out the call toB () `pueden haber sido optimizados.5, pero aparentemente Jonathon Reinhart tiene una explicación mucho mejor.Compile su código con
gcc -Wall filename.cVerá estas advertencias.In function 'B': 11:9: warning: variable 'a' set but not used [-Wunused-but-set-variable] In function 'A': 6:11: warning: 'a' is used uninitialized in this function [-Wuninitialized]En c La impresión de una variable no inicializada conduce a un comportamiento indefinido.
La sección 6.7.8 Inicialización del estándar C99 dice
— if it has pointer type, it is initialized to a null pointer; — if it has arithmetic type, it is initialized to (positive or unsigned) zero; — if it is an aggregate, every member is initialized (recursively) according to these rules; — if it is a union, the first named member is initialized (recursively) according to these rules.Editar1
Como @Jonathon Reinhart Si desactiva la optimización mediante el uso de la
-Obanderagcc-O0, es posible que obtenga la salida 5.Pero esto no es una buena idea, nunca lo use en el código de producción.
-WuninitializedEsta es una de las advertencias valiosas. Debería considerar esta. No debe desactivar ni omitir esta advertencia que genera un gran daño en la producción, como causar fallas mientras se ejecutan demonios.Editar2
Explicación de las diapositivas de Deep C ¿Por qué el resultado es 5 / basura? Agregando esta información de las diapositivas con modificaciones menores para que esta respuesta sea un poco más efectiva.
$ gcc -O0 file.c && ./a.out 5Quizás este compilador tiene un grupo de variables con nombre que reutiliza. Por ejemplo, se usó y se liberó la variable a
B(), luego, cuandoA()necesite un nombre enteroa, obtendrá la variable obtendrá la misma ubicación de memoria. Si cambia el nombre de la variableB()a, digamosb, entonces no creo que lo obtenga5.Pueden suceder muchas cosas cuando el optimizador se activa. En este caso, supongo que la llamada a
B()puede omitirse ya que no tiene efectos secundarios. Además, no me sorprendería queA()esté insertadomain(), es decir, sin llamada de función. (Pero dado queA ()tiene visibilidad de vinculador, el código de objeto para la función aún debe crearse en caso de que otro archivo de objeto quiera vincularse con la función). De todos modos, sospecho que el valor impreso será diferente si optimiza el código.gcc -O file.c && ./a.out 1606415608¡Basura!
fuente