简体   繁体   中英

How are initialized local variables stored in the stack memory?

Does the storing of local variables into stack memory, depends on using their values in function calls?

While doing some simple excercises with the "C" programming language and more specificly - with pointers, I've noticed the following anomaly ( I know it's not anomaly, it's just me with lack of proper understanding ) about initialized local varialbes.

When I define and initialize a couple of variables, and try to print the address (via the "printf()" function) of the first, and the last variable, I expect the address progression to corespond to the order of the listing of the variables. The first variable to have the highest address, and the last variable to occupy the (address of the first variable's memory block, minuns N memory blocks, where N is the count of the remaining variables, besides the first). Like that:

v1 = memory block 10;
v2 = memory block 09;
v3 = memory block 08;
v4 = memory block 07;

And when I'm making the program to print just the address of v1 and v4, I expect it to print:

Address of v1 is block 10;
Address of v4 is block 07;

Here comes to so-called "anomaly". When the program prints the address of these variables, it actually prints:

Address of v1 is block10; (as it should be)
Address of v4 is block09; (isn't v2 supposed to be stored here?)

Here is the code example:

#include <stdio.h>

int main()
{    
    char a = 1, b = 23, c = 123, d = 12;
        
    printf("address of a: %p\naddress of d: %p", &a, &d);

    return 0;
}

The output is:

address of a: 0x7fffa86fc724
address of d: 0x7fffa86fc725

Now, if I add a third variable as an argument in the "printf()" function, the address of the first variable remains the same, all three variables will share adjacent memory blocks, following the order of defining the variables. Let's take the above four variables v1, v2, v3 and v4. If I print the addresses of v1, v3 and v4, I receive the following result:

v1 = block10;
v3 = block09;
v4 = block08;

The order of listing the variable's addresses in the argument list of the "printf()" function has no effect on the ordering the addresses of the variables. I can see that the program still follows the order of defining the variables - the variable, which is defined first, will occupy the highest address, and every next variable, passed as argument to the function, will occuppy memory location, adjacent to the memory location of the previous variable, depending on the order of defining.

Code example:

#include <stdio.h>

int main()
{
    char a = 1, b = 23, c = 123, d = 12;
    
    printf("address of c: %p\naddress of a: %p\naddress of d: %p", &c, &a, &d);

    return 0;
}

Output will be:

address of c: 0x7ffd970e27d5
address of a: 0x7ffd970e27d4
address of d: 0x7ffd970e27d6

Furthermore, If the variables are being passed at least once as arguments to a function call (different function besides "printf()"), then printing the address of v1 and v4 only, will produce the output, which I initially expected.

Code example:

#include <stdio.h>

int main()
{
    char a, b, c, d;
    scanf(" %hhi %hhi %hhi %hhi", &a, &b, &c, &d);    
    
    printf("address of c: %p\naddress of a: %p\naddress of d: %p", &c, &a, &d);

    return 0;
}

Output will be:

address of a: 0x7ffeaac3c4a4
address of d: 0x7ffeaac3c4a7

Thus, I'm reaching the conclusion, that only a variables which are passed as an argument to a function call, are being stored in stack memory. What is happening, and more important - why is happening like this?

Does the compiler "throws out" of the program (during compilation process), variables, which despite being initialized with some value, are not used by any function as its arguments?

Optimizer is free to optimize as long as program behavior does not change (as-if rule), as specified by C standard (which is also why Undefined Behavior is so dangerous, it allows the compiler to "optimize" in unexpected ways).

For example, local variable can be in a register, unless you get its address, forcing compiler to place it in memory, so it has an address.

volatile forces compiler to assume every access to variable (read or write) can have a side effect, so disables many optimizations, and forces variable into memory (stack for local variable). That should solve your issue.

I expect the address progression to corespond to the order of the listing of the variables

Why? Nobody made any such guarantee, neither the K&R book nor the C standard. Furthermore, the stack pointer could either be increasing or decreasing depending on CPU and C allows either - in fact C says nothing about the stack what-so-ever. I've even programmed C on tiny micocontrollers without a stack.

all three variables will share adjacent memory blocks, following the order of defining the variables

Again, nobody made that guarantee. It is just one specific behavior on one particular compiler for one particular system.

  • The only guarantee C makes about adjacent allocation is when you use arrays.
  • The only guarantee C makes about order of allocation is when you use member variables inside a struct. (Though they are not guaranteed to be adjacent.)

Thus, I'm reaching the conclusion, that only a variables which are passed as an argument to a function call, are being stored in stack memory. What is happening, and more important - why is happening like this?

All variables that are used by your program has to be stored somewhere. They cannot be allocated in thin air.

As for what happens when you pass variables to a function, they are passed according to the ABI (Abstract Binary Interface) for a certain system, which specifies what parameters that are stacked/stored in registers and in what order, or if the stacking is done by caller or callee etc. Again, this is wildly system-specific and beyond the scope of C itself.


The main misunderstanding here is that you cannot investigate one particular program for a particular system, generated by one particular compiler, then draw conclusions about how C programs work in general.

You can however draw conclusions about how compiler x organises data and stack frames according to ABI y, for system z. If that's what you are interested in, your question need to mention those details.

  1. C does not know anything about the stack. How automatic variables are stored is left to the implementation.

  2. Compiler does not have to keep any variables or if they exist store them in a particular place. The observable behaviour of the program has to be exactly the same as abstract semantics would produce. How it is done internally is up to implementation with one exception: volatile objects.

So you can only discuss the particular implementation. The implementation may produce different code when different compile options are used (for example optimization level)

Thus, I'm reaching the conclusion, that only a variables which are passed as an argument to a function call, are being stored in stack memory. What is happening, and more important - why is happening like this?

Your conclusion is wrong. Example:

void foo(int x)
{
    int z = x;
    printf("%d\n", z);
}

int main(void)
{
    int y = 5;
    foo(y);
}

Code:

        .string "%d\n"
foo:
        mov     esi, edi
        xor     eax, eax
        mov     edi, OFFSET FLAT:.LC0
        jmp     printf
main:
        push    rax
        mov     edi, 5
        call    foo
        xor     eax, eax
        pop     rdx
        ret

https://godbolt.org/z/6Ye8WrM8M

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM