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 gcc
resultados -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 a
que 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 unaprintf
enB()
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
-O0
a 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 a
variable en[ebp-0x4]
5.En
A
Sin embargo, el compilador colocaint a
al[ebp-0xc]
. Entonces, en este caso, nuestras variables locales no terminaron en el mismo lugar. Al agregar unaprintf()
llamadaA
también, los marcos de pila paraA
yB
será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 variablea
no se inicializa, imprimir su valor conduce a un comportamiento indefinido.En algunos compiladores, la variable
a
inA
ya
inB
está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 to
B () `pueden haber sido optimizados.5
, pero aparentemente Jonathon Reinhart tiene una explicación mucho mejor.Compile su código con
gcc -Wall filename.c
Verá 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
-O
banderagcc-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.
-Wuninitialized
Esta 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 5
Quizá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