Will int declare a new variable in for loop?

Viewed 123

In each loop, will it be a totally new a or original a?

  for(int i=0;i<5;i++)
  {
      int a=i;
      printf("a=%d\n",a);
  }
}

The execution result:

a=0
a=1
a=2
a=3
a=4
5 Answers

There are two things:

  1. The value of a will be removed after the closing braces of the loop, so yeah it will be a totally new variable

  2. But when it comes to talk about from the perspective of compiler, it is not so, the value might still be residing in some register (IT IS NOT FLUSHED OUT)

The lifetime of a is only until the closing brace of the for-loop.

for(int i=0;i<5;i++)
  {
      int a=i;                    //a new variable a will be created, initialized with the value of i 
      printf("a=%d\n",a);
  }                               // lifetime of the variable a will end, forget everything

So in each run through the loop there will be a new variable a.

Yes, in each iteration of the loop, there will be a new a whose existence only lasts to the closing brace of the loop. Next iteration another a will be created.

In the code snippet you provided, these new created a will be assigned to the same address for some reason. However, this is not a guaranteed behavior and you can not assume it.

#include <stdio.h>

int main(void) {
  for (int i = 0; i < 5; i++) {
    int a = i;
    printf("a=%d %p\n", a, &a);
  }

  return 0;
}

On my computer all the installed compilers show similar behavior: these new created a will be assigned to the same address.

a=0 0000008F898FFB44
a=1 0000008F898FFB44
a=2 0000008F898FFB44
a=3 0000008F898FFB44
a=4 0000008F898FFB44

Again, this is not a guaranteed behavior and you can not assume it.

for(int i=0;i<5;i++) //for loop will have 5 iterations starting at i value 0 - 4
  {
      int a=i; //each iteration a "new" variable "a" is declared and initialized to the value of i
      printf("a=%d\n",a); //prints the value of "a" to the standard out
  }
}

Your variable "a" resides inside the scope of the for loop block. Each iteration, that block will execute; at the end of the iteration, the block (everything inside curly braces) is discarded (may still be accessible somewhere in memory) and the block is executed once more with the new iteration. This means that the variable "a" is discarded at the end of the iteration and declared and initialized at the beginning of the new iteration (as a "new" variable).

in order to understand what happens I used a sample code which is :

#include <stdio.h>
int main()
{
  for(int i=0;i<5;i++)
  {
      int a=i;
      printf("a=%x\n",&a);
  }

    return 0;
}

and I generated assembly file produced by gcc using this command line gcc -fverbose-asm main1.c -S -o main1.s. and here is the file output :

        .file   "main1.c"
 # GNU C17 (MinGW.org GCC Build-2) version 9.2.0 (mingw32)
 #  compiled by GNU C version 9.2.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.21-GMP

 # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 # options passed:  -iprefix c:\mingw\bin\../lib/gcc/mingw32/9.2.0/ main1.c
 # -mtune=generic -march=i586 -auxbase-strip main1.s -fverbose-asm
 # options enabled:  -faggressive-loop-optimizations -fassume-phsa
 # -fasynchronous-unwind-tables -fauto-inc-dec -fcommon
 # -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
 # -feliminate-unused-debug-types -ffp-int-builtin-inexact -ffunction-cse
 # -fgcse-lm -fgnu-runtime -fgnu-unique -fident -finline-atomics
 # -fipa-stack-alignment -fira-hoist-pressure -fira-share-save-slots
 # -fira-share-spill-slots -fivopts -fkeep-inline-dllexport
 # -fkeep-static-consts -fleading-underscore -flifetime-dse
 # -flto-odr-type-merging -fmath-errno -fmerge-debug-strings -fpeephole
 # -fplt -fprefetch-loop-arrays -freg-struct-return
 # -fsched-critical-path-heuristic -fsched-dep-count-heuristic
 # -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
 # -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
 # -fsched-stalled-insns-dep -fschedule-fusion -fsemantic-interposition
 # -fset-stack-executable -fshow-column -fshrink-wrap-separate
 # -fsigned-zeros -fsplit-ivs-in-unroller -fssa-backprop -fstdarg-opt
 # -fstrict-volatile-bitfields -fsync-libcalls -ftrapping-math
 # -ftree-cselim -ftree-forwprop -ftree-loop-if-convert -ftree-loop-im
 # -ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
 # -ftree-phiprop -ftree-reassoc -ftree-scev-cprop -funit-at-a-time
 # -funwind-tables -fverbose-asm -fzero-initialized-in-bss -m32 -m80387
 # -m96bit-long-double -maccumulate-outgoing-args -malign-double
 # -malign-stringops -mavx256-split-unaligned-load
 # -mavx256-split-unaligned-store -mfancy-math-387 -mfp-ret-in-387
 # -mieee-fp -mlong-double-80 -mms-bitfields -mno-red-zone -mno-sse4
 # -mpush-args -msahf -mstack-arg-probe -mstv -mvzeroupper

    .text
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "a=%x\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB13:
    .cfi_startproc
    pushl   %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp   #,
    .cfi_def_cfa_register 5
    andl    $-16, %esp   #,
    subl    $32, %esp    #,
 # main1.c:3: {
    call    ___main  #
 # main1.c:4:   for(int i=0;i<5;i++)
    movl    $0, 28(%esp)     #, i
 # main1.c:4:   for(int i=0;i<5;i++)
    jmp L2   #
L3:
 # main1.c:6:       int a=i;
    movl    28(%esp), %eax   # i, tmp84
    movl    %eax, 24(%esp)   # tmp84, a
 # main1.c:7:       printf("a=%x\n",&a);
    leal    24(%esp), %eax   #, tmp85
    movl    %eax, 4(%esp)    # tmp85,
    movl    $LC0, (%esp)     #,
    call    _printf  #
 # main1.c:4:   for(int i=0;i<5;i++)
    addl    $1, 28(%esp)     #, i
L2:
 # main1.c:4:   for(int i=0;i<5;i++)
    cmpl    $4, 28(%esp)     #, i
    jle L3   #,
 # main1.c:10:  return 0;
    movl    $0, %eax     #, _5
 # main1.c:11: }
    leave   
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
LFE13:
    .ident  "GCC: (MinGW.org GCC Build-2) 9.2.0"
    .def    _printf;    .scl    2;  .type   32; .endef

notice that from following lines of code in the assembly file :

 # main1.c:6:       int a=i;
movl    28(%esp), %eax   # i, tmp84
movl    %eax, 24(%esp)   # tmp84, a

which means that local variable named a is stored in stack at byte number 24 from stack pointer and the local variable named i is stored in stack at byte position number 28 .

so let's make other version of the code where the new code is :

    #include <stdio.h>
int main()
{
    for(int i=0;i<5;i++)
    {
        int a=i;
        printf("a=%x\n",&a);
    }
    int y = 10;
    int a = 5;

    return 0;
}

and the new generated assembly file is :

        .file   "main1.c"
 # GNU C17 (MinGW.org GCC Build-2) version 9.2.0 (mingw32)
 #  compiled by GNU C version 9.2.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.21-GMP

 # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 # options passed:  -iprefix c:\mingw\bin\../lib/gcc/mingw32/9.2.0/ main1.c
 # -mtune=generic -march=i586 -auxbase-strip main2.s -fverbose-asm
 # options enabled:  -faggressive-loop-optimizations -fassume-phsa
 # -fasynchronous-unwind-tables -fauto-inc-dec -fcommon
 # -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
 # -feliminate-unused-debug-types -ffp-int-builtin-inexact -ffunction-cse
 # -fgcse-lm -fgnu-runtime -fgnu-unique -fident -finline-atomics
 # -fipa-stack-alignment -fira-hoist-pressure -fira-share-save-slots
 # -fira-share-spill-slots -fivopts -fkeep-inline-dllexport
 # -fkeep-static-consts -fleading-underscore -flifetime-dse
 # -flto-odr-type-merging -fmath-errno -fmerge-debug-strings -fpeephole
 # -fplt -fprefetch-loop-arrays -freg-struct-return
 # -fsched-critical-path-heuristic -fsched-dep-count-heuristic
 # -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
 # -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
 # -fsched-stalled-insns-dep -fschedule-fusion -fsemantic-interposition
 # -fset-stack-executable -fshow-column -fshrink-wrap-separate
 # -fsigned-zeros -fsplit-ivs-in-unroller -fssa-backprop -fstdarg-opt
 # -fstrict-volatile-bitfields -fsync-libcalls -ftrapping-math
 # -ftree-cselim -ftree-forwprop -ftree-loop-if-convert -ftree-loop-im
 # -ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
 # -ftree-phiprop -ftree-reassoc -ftree-scev-cprop -funit-at-a-time
 # -funwind-tables -fverbose-asm -fzero-initialized-in-bss -m32 -m80387
 # -m96bit-long-double -maccumulate-outgoing-args -malign-double
 # -malign-stringops -mavx256-split-unaligned-load
 # -mavx256-split-unaligned-store -mfancy-math-387 -mfp-ret-in-387
 # -mieee-fp -mlong-double-80 -mms-bitfields -mno-red-zone -mno-sse4
 # -mpush-args -msahf -mstack-arg-probe -mstv -mvzeroupper

    .text
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "a=%x\12\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB13:
    .cfi_startproc
    pushl   %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp   #,
    .cfi_def_cfa_register 5
    andl    $-16, %esp   #,
    subl    $32, %esp    #,
 # main1.c:3: {
    call    ___main  #
 # main1.c:4:     for(int i=0;i<5;i++)
    movl    $0, 28(%esp)     #, i
 # main1.c:4:     for(int i=0;i<5;i++)
    jmp L2   #
L3:
 # main1.c:6:         int a=i;
    movl    28(%esp), %eax   # i, tmp84
    movl    %eax, 16(%esp)   # tmp84, a
 # main1.c:7:         printf("a=%x\n",&a);
    leal    16(%esp), %eax   #, tmp85
    movl    %eax, 4(%esp)    # tmp85,
    movl    $LC0, (%esp)     #,
    call    _printf  #
 # main1.c:4:     for(int i=0;i<5;i++)
    addl    $1, 28(%esp)     #, i
L2:
 # main1.c:4:     for(int i=0;i<5;i++)
    cmpl    $4, 28(%esp)     #, i
    jle L3   #,
 # main1.c:9:     int y = 10;
    movl    $10, 24(%esp)    #, y
 # main1.c:10:  int a = 5;
    movl    $5, 20(%esp)     #, a
 # main1.c:12:     return 0;
    movl    $0, %eax     #, _7
 # main1.c:13: }
    leave   
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
LFE13:
    .ident  "GCC: (MinGW.org GCC Build-2) 9.2.0"
    .def    _printf;    .scl    2;  .type   32; .endef

now notice the following lines :

 # main1.c:6:         int a=i;
movl    28(%esp), %eax   # i, tmp84
movl    %eax, 16(%esp)   # tmp84, a

which means that local variable named a inside loop is stored in stack at byte number 16 from stack pointer base and the local variable named i is stored in stack at byte position number 28 offset from base esp register.

after the loop ends there are 2 other local variables created which are a and y from the following lines of assembly code :

# main1.c:9:     int y = 10;
movl    $10, 24(%esp)    #, y
# main1.c:10:  int a = 5;
movl    $5, 20(%esp)     #, a

this means that variable a and y using addresses 20 and 24 offset from stack pointer and not reusing the destroyed places of previous local variables named a and i , so why is that ?

let's take a look to another code example :

    #include <stdio.h>
int main()
{
    int *ptr;
    for(int i=0;i<5;i++)
    {
        int a=10;
        ptr = &a;
        int x;
    }
    int y = 10;

    printf("a = %d\n",*ptr); // how come a = 10?
    return 0;
}

in this code , I made a dangling pointer and notice the output : enter image description here

so it means that gcc isn't reusing destroyed local variables in stack , Right ? actually no see the following C code:

    void func2()
{
    for(int i=0;i<5;i++)
    {
        int a=10;
        int x;
    }
}

void func1()
{
    for(int i=0;i<5;i++)
    {
        int a=10;
        int x;
        
    }
}

int main()
{
    func1();
    func2();
    int y = 10;

    return 0;
}

and here is the assembly equivalence of it :

        .file   "main1.c"
 # GNU C17 (MinGW.org GCC Build-2) version 9.2.0 (mingw32)
 #  compiled by GNU C version 9.2.0, GMP version 6.1.2, MPFR version 4.0.2, MPC version 1.1.0, isl version isl-0.21-GMP

 # GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 # options passed:  -iprefix c:\mingw\bin\../lib/gcc/mingw32/9.2.0/ main1.c
 # -mtune=generic -march=i586 -auxbase-strip main2.s -fverbose-asm
 # options enabled:  -faggressive-loop-optimizations -fassume-phsa
 # -fasynchronous-unwind-tables -fauto-inc-dec -fcommon
 # -fdelete-null-pointer-checks -fdwarf2-cfi-asm -fearly-inlining
 # -feliminate-unused-debug-types -ffp-int-builtin-inexact -ffunction-cse
 # -fgcse-lm -fgnu-runtime -fgnu-unique -fident -finline-atomics
 # -fipa-stack-alignment -fira-hoist-pressure -fira-share-save-slots
 # -fira-share-spill-slots -fivopts -fkeep-inline-dllexport
 # -fkeep-static-consts -fleading-underscore -flifetime-dse
 # -flto-odr-type-merging -fmath-errno -fmerge-debug-strings -fpeephole
 # -fplt -fprefetch-loop-arrays -freg-struct-return
 # -fsched-critical-path-heuristic -fsched-dep-count-heuristic
 # -fsched-group-heuristic -fsched-interblock -fsched-last-insn-heuristic
 # -fsched-rank-heuristic -fsched-spec -fsched-spec-insn-heuristic
 # -fsched-stalled-insns-dep -fschedule-fusion -fsemantic-interposition
 # -fset-stack-executable -fshow-column -fshrink-wrap-separate
 # -fsigned-zeros -fsplit-ivs-in-unroller -fssa-backprop -fstdarg-opt
 # -fstrict-volatile-bitfields -fsync-libcalls -ftrapping-math
 # -ftree-cselim -ftree-forwprop -ftree-loop-if-convert -ftree-loop-im
 # -ftree-loop-ivcanon -ftree-loop-optimize -ftree-parallelize-loops=
 # -ftree-phiprop -ftree-reassoc -ftree-scev-cprop -funit-at-a-time
 # -funwind-tables -fverbose-asm -fzero-initialized-in-bss -m32 -m80387
 # -m96bit-long-double -maccumulate-outgoing-args -malign-double
 # -malign-stringops -mavx256-split-unaligned-load
 # -mavx256-split-unaligned-store -mfancy-math-387 -mfp-ret-in-387
 # -mieee-fp -mlong-double-80 -mms-bitfields -mno-red-zone -mno-sse4
 # -mpush-args -msahf -mstack-arg-probe -mstv -mvzeroupper

    .text
    .globl  _func2
    .def    _func2; .scl    2;  .type   32; .endef
_func2:
LFB0:
    .cfi_startproc
    pushl   %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp   #,
    .cfi_def_cfa_register 5
    subl    $16, %esp    #,
 # main1.c:3:         for(int i=0;i<5;i++)
    movl    $0, -4(%ebp)     #, i
 # main1.c:3:         for(int i=0;i<5;i++)
    jmp L2   #
L3:
 # main1.c:5:             int a=10;
    movl    $10, -8(%ebp)    #, a
 # main1.c:3:         for(int i=0;i<5;i++)
    addl    $1, -4(%ebp)     #, i
L2:
 # main1.c:3:         for(int i=0;i<5;i++)
    cmpl    $4, -4(%ebp)     #, i
    jle L3   #,
 # main1.c:8:     }
    nop 
    nop 
    leave   
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
LFE0:
    .globl  _func1
    .def    _func1; .scl    2;  .type   32; .endef
_func1:
LFB1:
    .cfi_startproc
    pushl   %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp   #,
    .cfi_def_cfa_register 5
    subl    $16, %esp    #,
 # main1.c:12:         for(int i=0;i<5;i++)
    movl    $0, -4(%ebp)     #, i
 # main1.c:12:         for(int i=0;i<5;i++)
    jmp L5   #
L6:
 # main1.c:14:             int a=10;
    movl    $10, -8(%ebp)    #, a
 # main1.c:12:         for(int i=0;i<5;i++)
    addl    $1, -4(%ebp)     #, i
L5:
 # main1.c:12:         for(int i=0;i<5;i++)
    cmpl    $4, -4(%ebp)     #, i
    jle L6   #,
 # main1.c:18:     }
    nop 
    nop 
    leave   
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
LFE1:
    .def    ___main;    .scl    2;  .type   32; .endef
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB2:
    .cfi_startproc
    pushl   %ebp     #
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp   #,
    .cfi_def_cfa_register 5
    andl    $-16, %esp   #,
    subl    $16, %esp    #,
 # main1.c:21:     {
    call    ___main  #
 # main1.c:22:         func1();
    call    _func1   #
 # main1.c:23:      func2();
    call    _func2   #
 # main1.c:24:         int y = 10;
    movl    $10, 12(%esp)    #, y
 # main1.c:26:         return 0;
    movl    $0, %eax     #, _5
 # main1.c:27:     }
    leave   
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret 
    .cfi_endproc
LFE2:
    .ident  "GCC: (MinGW.org GCC Build-2) 9.2.0"

if you notice well , you will see this lines of code :

     # main1.c:12:         for(int i=0;i<5;i++)
    movl    $0, -4(%ebp)     #, i
 # main1.c:12:         for(int i=0;i<5;i++)
    jmp L5   #
L6:
 # main1.c:14:             int a=10;
    movl    $10, -8(%ebp)    #, a
 # main1.c:12:         for(int i=0;i<5;i++)
    addl    $1, -4(%ebp)     #, i
L5:
 # main1.c:12:         for(int i=0;i<5;i++)
    cmpl    $4, -4(%ebp)     #, i
    jle L6   #,
 # main1.c:18:     }

these line are in func1 where a and i are store in position -8 and -4 respectively , x doesn't have position because it's not initialized and not used , but anyways ,

for func2:

     # main1.c:3:         for(int i=0;i<5;i++)
    movl    $0, -4(%ebp)     #, i
 # main1.c:3:         for(int i=0;i<5;i++)
    jmp L2   #
L3:
 # main1.c:5:             int a=10;
    movl    $10, -8(%ebp)    #, a
 # main1.c:3:         for(int i=0;i<5;i++)
    addl    $1, -4(%ebp)     #, i
L2:
 # main1.c:3:         for(int i=0;i<5;i++)
    cmpl    $4, -4(%ebp)     #, i
    jle L3   #,
 # main1.c:8:     }

it's using same addresses for both variables a and i , so yes it's reusing addresses , also to emphasis my point , look to the next code example and it's output :

    #include <stdio.h>

    void func2()
    {
        int x = 10;
        printf("address of x = %x\n" , &x);
    }

    void func1()
    {
        int y = 5;
        printf("address of y = %x\n" , &y);
    }

    int main()
    {

        func1();
        func2();
        int y = 10;

        return 0;
    }

and here is the output : enter image description here

so the local variables are destroyed and created every time you call the function.

Related