¿Por qué este programa produce una salida extraña cuando la variable no está inicializada?

5 minutos de lectura

avatar de usuario
Milán

int main() {
    int j = 0;
    int i = 0;
    for (j = 0; j < 5; j++) {
        printf("Iteration %d :  %d ", j + 1, i);
        int i;
        printf("%d", i);
        i = 5;
        printf("\n");
    }
}

El código anterior genera el siguiente resultado:

Iteration 1 :  0 0
Iteration 2 :  0 5
Iteration 3 :  0 5
Iteration 4 :  0 5
Iteration 5 :  0 5

No soy capaz de entender por qué el segundo printf el valor en las iteraciones 2,3,4,5 es 5.

Mi comprensión de por qué el primer valor es 0 en cada iteración es que el alcance de i en el for loop es local y se destruye tan pronto como entramos en una nueva iteración como i fue declarado en el for círculo.

Pero no puedo entender por qué este valor se convierte en 5 en el segundo printf.

  • Buena pregunta, lástima que la respuesta sea tan breve.

    – Betsabé

    23 de febrero de 2017 a las 15:28

  • @Bathsheba: siempre puede hacer que su respuesta sea intencionalmente detallada: P

    – StoryTeller – Unslander Mónica

    23 de febrero de 2017 a las 15:32

  • Nota: esto se denomina “sombreado” y se desaconseja encarecidamente, ya que no sirve de nada, pero es muy propenso a errores. Habilite todas las advertencias recomendadas del compilador, una de ellas debería advertir sobre tales definiciones problemáticas.

    – demasiado honesto para este sitio

    23 de febrero de 2017 a las 15:33

  • Tiene razón en que queda fuera del alcance en cada iteración, pero generalmente la siguiente iteración usará la misma memoria que la anterior, por qué no, dando la apariencia de estar inicializada

    – Físico loco

    23 de febrero de 2017 a las 15:33

  • Agregando al comentario de @Olaf, para GCC -Wshadow.

    – Iharob Al Asimi

    23 de febrero de 2017 a las 15:34


avatar de usuario
Betsabé

El comportamiento de su programa es indefinido.

El ámbito interior i no es inicializado en el momento en que se lee.

(Qué puede que estar pasando es que el reintroducido i en iteraciones posteriores ocupa la misma memoria que la encarnación anterior del interior iy la memoria no inicializada en la primera iteración corresponde a 0. Pero no confía en eso. En otras ocasiones, el compilador podría comerse a tu gato).

  • No, int puede contener una representación trampa. Si era unsigned char entonces estarías en lo cierto.

    – Betsabé

    23 de febrero de 2017 a las 15:29

  • @Bathsheba ni siquiera sería correcto entonces. No tiene su dirección tomada, por lo que tiene un comportamiento indefinido. Y si tuviera su dirección tomada, tendría un valor indeterminado (incluso si fuera un carácter sin firmar) y usarlo en una función de biblioteca tendría un comportamiento indefinido.

    – Antti Haapala — Слава Україні

    23 de febrero de 2017 a las 15:33

  • Siempre es UB según esto.

    – Lundin

    23 de febrero de 2017 a las 15:36

  • Superando al caballo muerto, ya vinculé una respuesta que indica por qué siempre es UB. No importa la representación de trampas o sin representación de trampas. Como dice Antti, es porque es una variable automática que no tiene su dirección tomada. C11 6.3.2.1.

    – Lundin

    23 de febrero de 2017 a las 15:38


  • “En otras ocasiones, el compilador podría comerse a tu gato”. ¡Oh vamos! ¡No esa mierda otra vez, compilador! Ya te comiste 50 gatos míos en la última hora. No es de extrañar que haya ganado tanto tamaño de instalación a lo largo de los años con todos esos gatos que está comiendo.

    – Kaiserludi

    23 de febrero de 2017 a las 18:04

avatar de usuario
Hormiga

El segundo printf en su programa está imprimiendo un valor basura de una variable local no inicializada i. En general, el comportamiento es indefinido.

Por accidente, la ubicación de almacenamiento que representa su i (celda de memoria o registro de CPU) es el mismo en cada iteración del ciclo. Por otro accidente, el cuerpo de su ciclo se ejecuta como una declaración compuesta única para cada iteración (en lugar de desenrollar el ciclo y ejecutar todas las iteraciones al mismo tiempo de manera intercalada). Por otro accidente, la ubicación de almacenamiento de i mantiene su antiguo valor de la iteración anterior. Entonces, la basura que está imprimiendo coincide con el último valor almacenado de esa ubicación en la iteración anterior del ciclo.

por eso ves 5 en local i en cada iteración además de la primera. En la primera iteración, ese valor de basura resultó ser 0.

Creo que esto es lo que está pasando:

Breakpoint 1, main () at abc.c:4
4       int j = 0;
(gdb) s
5       int i = 0;
(gdb)
6       for(j=0;j<5;j++){
(gdb) p &i
$23 = (int *) 0x7fff5fbffc04           //(1) (addr = 0x7fff5fbffc04) i = 0
(gdb) p i
$24 = 0                                              // (2) i == 0
(gdb) s
7           printf("Iteration %d :  %d ",j+1,i);
(gdb) p &i
$25 = (int *) 0x7fff5fbffc00            //(3) here compiler finds there is contention in variable 'i' and assigns the inner one which is in present scope. Now, when subroutines are called, the stack frame is populated with (arguments, return address and local variables) and this is when the inner 'i' was also got allocated in this inner loop block but not initialized yet and this is the reason i get garbage value in the first integration output.

(gdb) p i
$26 = 1606417440                          // (4)  Here i == 1606417440 (Garbage)
(gdb) s
9           printf("%d",i);
(gdb) s
10          i = 5;
(gdb) p &i
$27 = (int *) 0x7fff5fbffc00
(gdb) p i
$28 = 1606417440
(gdb) s
11          printf("\n");
(gdb) p &i
$29 = (int *) 0x7fff5fbffc00
(gdb) p i                                                //(5) after executing previous statement, now i == 5
$30 = 5
(gdb) 

Si declara una variable local y no le da un valor antes de usarla, si obtiene un comportamiento indefinido.

C estándar decir,

Las variables con duración de almacenamiento automático se inicializan cada vez que se ejecuta su declaración-sentencia. Las variables con duración de almacenamiento automático declaradas en el bloque se destruyen al salir del bloque.

¿Ha sido útil esta solución?