6

In each loop iteration, variable j is declared again and again. Then why is its address remaining same?

  • Shouldn't it be given some random address each time?
  • Is this compiler dependent?
#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int j;
        printf("%p\n", &j);
    }
    return 0;
}

Testrun:-

shadyabhi@shadyabhi-desktop:~/c$ gcc test.c
shadyabhi@shadyabhi-desktop:~/c$ ./a.out
0x7fffc0b8e138
0x7fffc0b8e138
0x7fffc0b8e138
shadyabhi@shadyabhi-desktop:~/c$
shadyabhi
  • 16,675
  • 26
  • 80
  • 131
  • I posted the wrong question by mistake.. My bad.. sorry.. I have updated the question – shadyabhi Mar 15 '10 at 14:35
  • @Gardener: it will compile fine. `void *` will be `cast` ed automatically. – N 1.1 Mar 15 '10 at 14:38
  • You have now changed the question. Have you tested it to see if your original assertion is still true? It is not even valid code, so the answer to that is almost certainly no. – Clifford Mar 15 '10 at 14:39
  • @nvl: Implicit cast from void* to int!? I hope not. – Clifford Mar 15 '10 at 14:40
  • 2
    Maybe we should wait until this question is stable before further comment!? ;) – Clifford Mar 15 '10 at 14:48
  • The question was wrongly posted.. I cant delete the question.. Users with appropriate privilege pls delete the question.. The answers posted are not according to the question.... – shadyabhi Mar 15 '10 at 14:52
  • @Clifford: yeah! malloc has no problem with implicit cast. check yourself. I always use it implicitly. @shadyabhi: if you use `malloc`, `j` will be allocated different memory each time, only if you dont `free` it each time. – N 1.1 Mar 15 '10 at 14:58
  • Version 3 of the question still is not malloc'ing the j variable, so maybe this is not yet the final version of the question? If you really want to use malloc, consider something like int *j = (int * ) malloc(sizeof(int)*1); and then free it. – Francesco Mar 15 '10 at 15:03
  • @nvl: It is not worth debating now the OP has changed the question so many times and rendered all answers and comments nonsensical! At the point I made my comment, the assignment was of a void pointer to an int (not an int*). However you are right, in VC++ 2008 C compilation it produces a warning not an error (warnings are errors IMO however); while I am sure that you implicitly cast a void* to another pointer type all the time, I doubt you'd let it do that for a non-pointer would you? – Clifford Mar 16 '10 at 10:28
  • @Clifford: I use implicit cast only with `malloc` because GCC does implicit cast and does not throw any warning because its totally safe to do so. Its needless to cast a pointer returned by `malloc` (in C). Check out http://stackoverflow.com/questions/1942159/need-some-clarification-regarding-casting-in-c/1942276#1942276 http://stackoverflow.com/questions/1835193/is-it-a-better-practice-to-typecast-the-pointer-returned-by-malloc/1835201#1835201 http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858 Did i make myself clear now ? :) – N 1.1 Mar 16 '10 at 10:54
  • @nvl: You always were clear, it is the OP who obfuscated it by frequently changing the question. You seem to have missed my point that we were probably talking about different versions of the question and do not disagree at all. The version I was referring to was Revision 1 which was briefly rolled back it seems, and which assigned an int j with malloc() but displayed &j - obviously nonsense. This revision: http://stackoverflow.com/revisions/b36c39a0-f3c9-47b7-88cb-b95a18fe3ae2/view-source to be clear. – Clifford Mar 16 '10 at 20:34

11 Answers11

12

It is memory on the stack. It is not allocated from the heap. The stack would not change in that loop.

Mark Wilkins
  • 40,729
  • 5
  • 57
  • 110
6

Why should it be different? The compiler needs space on the stack to store an int, and each time it goes through the loop the same space is available.

By the way, you're not actually using malloc at all. j is kept on the stack.

6

The reason why the address of j never changes is because the compiler allocates memory for j on the stack when the function is entered as opposed to when j comes into scope.

As always, looking at some assembly code might help explain the concept. Take the following function:-

int foo(void)
   {
   int i=3;
   i++;
      {
      int j=2;
      i=j;
      }
   return i;
   }

gcc converts this to the following x86 assembly code:-

foo:
    pushl   %ebp                 ; save stack base pointer
    movl    %esp, %ebp           ; set base pointer to old top of stack
    subl    $8, %esp             ; allocate memory for local variables
    movl    $3, -4(%ebp)         ; initialize i
    leal    -4(%ebp), %eax       ; move address of i into eax
    incl    (%eax)               ; increment i by 1
    movl    $2, -8(%ebp)         ; initialize j
    movl    -8(%ebp), %eax       ; move j into accumulator
    movl    %eax, -4(%ebp)       ; set i to j
    movl    -4(%ebp), %eax       ; set the value of i as the function return value
    leave                        ; restore stack pointers
    ret                          ; return to caller

Let's walk through this assembly code. The first line saves the current stack base pointer so that it can be restored when the function exits, the second line sets the current top of the stack to be the new stack base pointer for this function.

The third line is the one that allocates the memory on the stack for all the local variables. The instruction subl $8, %esp subtracts 8 from the current top of the stack pointer, the esp register. Stacks grow down in memory so this line of code actually increases the memory on the stack by 8 bytes. We have two integers in this function, i and j, each of which require 4 bytes, hence why it allocates 8 bytes.

Line 4 initializes i to 3 by directly writing to an address on the stack. Lines 5 and 6 then load and increment i. Line 7 initializes j by writing the value 2 into the memory allocated for j on the stack. Note that when j came into scope at line 7 the assembly code did not adjust the stack to allocate memory for it, that had already been taken care of earlier.

I'm sure it's obvious, but the reason why the compiler allocates the memory for all the local variables at the start of the function is because it is way more efficient to do so. Adjusting the stack each time a local variable went in or out of scope would result in a lot of unnecessary manipulations of the stack pointer for no gain.

I'm sure you can work out what the rest of the assembly code does yourself, if not post a comment and I'll walk you through it.

Andrew O'Reilly
  • 1,645
  • 13
  • 15
  • Just in case, if someone needs to know how to get the assembly code.. Use $gcc -S foo.c – shadyabhi Mar 15 '10 at 20:55
  • @Andrew - Pls tell a nice source to learn Assembly language. I only have knowledge of LC3. Now, I want to understand assembly codes for lets say Core2Quad. – shadyabhi Mar 15 '10 at 21:02
  • I just saw the assembly code for function foo by doing $gcc -S foo.c & the assembly code was too cryptic. The code was very different from x86 code. I am running gcc x86_64 (ubuntu) on a Core2Quad Machine.. – shadyabhi Mar 15 '10 at 21:05
  • @shadyabhi - Dr Paul Carter's "PC Assembly Language" book is a great introduction to x86 assembly code and be downloaded for free from here http://www.drpaulcarter.com/pcasm/. Randall Hyde has a free pdf appendix to his Writing Great Code Vol 2 book that describes the core x86 instruction set, http://homepage.mac.com/randyhyde/webster.cs.ucr.edu/www.writegreatcode.com/Vol2/wgc2_OA.pdf. You can use this along with gcc -S (or gdb's disassemble command) to explore the assembly code that compilers emit. – Andrew O'Reilly Mar 15 '10 at 21:38
  • What about x86_64? Bcos I have 64 bit ubuntu.. How can I understand the output og "gcc -S"? – shadyabhi Mar 15 '10 at 21:40
  • I haven't had the time to learn x86_x64 yet so I don't have anything I'd recommend I'm afraid. From what I understand x64 is quite similar to x86 so an investment in learning x86 is not wasted. You can put gcc in x86 mode on a x64 system via the -m32 option. When I do get around to learning x64 I'll probably grab an instruction set summary from somewhere and start walking through the assembly code created by gcc for a variety of usecases. – Andrew O'Reilly Mar 15 '10 at 22:11
4

j and i are allocated on the stack, not on the heap or freestore (which would require a malloc or new, respectively). The stack puts the next variable in a deterministic location (the top of the stack), and so it always has the same address. Though if you are running in optimized mode, the variable is probably never "dealloced", that is, the stack size isn't changing throughout your program, because it would just be wasted cycles.

Todd Gardner
  • 13,313
  • 39
  • 51
2

j is allocated on the stack, so during one call of that function, it will always have the same address.

If you called main() from within that loop, the "inner" main's j would have a different address, as it would be higher on the stack.

See Hardware Stack on Wikipedia for more details.

Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
2

Actually you are not using malloc so what's the problem?

The variable is a local to the function, and its space is reserved on the stack during compilation.. so why should it reallocate it on every iteration? Just because it's declared inside the loop?

Jack
  • 131,802
  • 30
  • 241
  • 343
1

You are not malloc-ing. Its an stack address so its the same always because its always in the same place of the stack once and again.

Arkaitz Jimenez
  • 22,500
  • 11
  • 75
  • 105
1

It is declared within the loop as you say, but it both goes out of scope and is 'destroyed' at the end of each iteration (that is it is not in scope and does not exist when the loop condition is tested). Therefore it is perfectly legitimate for the same stack location to be reused (in fact it would be an error if this were not the case).

Clifford
  • 88,407
  • 13
  • 85
  • 165
0

Hint: What do you think this will do?

#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int j = 42;
        printf("%p\n", &j);
    }
    return 0;
}
geocar
  • 9,085
  • 1
  • 29
  • 37
0

As other answers have said, you're not allocing here anything but on stack. But even if you modify code as follows, it will not necessarily change the allocation address.

This depends on libc that is used, malloc is usually located there, but some applications (most notably firefox) override it for their use (memory fragmentation issues etc.).

#include<stdio.h>
#include<malloc.h>

int main()
{
    int i=3;
    while (i--)
    {
        int *j = (int *) malloc(sizeof(int));
        printf("%p\n", j);
        free (j);
    }
    return 0;
}

if you comment out the free(j) you'll note that j's address does change. But depending on your libc the j's address may always change.

Pasi Savolainen
  • 2,460
  • 1
  • 22
  • 35
-1

Now, you'd get a series of leaked allocations since the j's are not stored for subsequent free's. j wouldn't necesserily get random address but probably just as a sequence compared to the previous allocations of j.

If you would free j in the end of the loop, you could get the same behavior as before depending on the implementation of malloc and free.

Edit: you may want to recheck the printed values with this code.

Tobias Wärre
  • 793
  • 5
  • 11