Michael
Michael

Reputation: 135

Memory allocation in stack

I have the following code:

#include <iostream>
using namespace std;
int main()
{   
    int i, j;
    int *p = &i;    
            
    cout << "Address of i = " << &i << endl;
    cout << "Address of j = " << &j << ", which is " << &i - 1 << endl;
    cout << "Address of p = " << &p << ", which is NOT " << &j - 1 << endl;
}

and I got the following output (VS Code, with GCC):

Address of i = 0x61fe1c
Address of j = 0x61fe18, which is 0x61fe18
Address of p = 0x61fe10, which is NOT 0x61fe14

I thought local variables, like int or pointer, are allocated continuously in the stack. So I am expecting

Address of i = 0x61fe1c
Address of j = 0x61fe18
Address of p = 0x61fe14

What am I missing here?

EDIT: I think I got it. The pointer points to the lower address of the variable.

#include <iostream>
using namespace std;
int main()
{   
    int i, j;
    int *p;
    int k, x;
            
    cout << "Address of i = " << &i << endl;
    cout << "Address of j = " << &j << ", which is " << &p + 1 << endl;
    cout << "Address of p = " << &p << ", which is " << &k + 1 << endl;
    cout << "Address of k = " << &k << ", which is " << &x + 1 << endl;     
}   

This gives me

Address of i = 0x61fe1c
Address of j = 0x61fe18, which is 0x61fe18
Address of p = 0x61fe10, which is 0x61fe10
Address of k = 0x61fe0c, which is 0x61fe0c

as expected.

Upvotes: 0

Views: 309

Answers (1)

Jerry Coffin
Jerry Coffin

Reputation: 490653

You're not really missing anything, as such, but your expectation has little basis.

Most compilers will typically use what's called "natural alignment", which means the size of the item is also its required alignment. For example, if you allocate a 4-byte item, it'll be allocated at an address that's a multiple of 4. Likewise, an 8-byte item, it'll be at an address that's a multiple of 8.

In some cases, alignment is even stricter than that, so (for example) a 16-byte item might need to be aligned to an address that's a multiple of 32. This is particularly common for "vectored" types, that hold multiple operands in a single large chunk, and operate on the multiple operands simultaneously (e.g., Intel AVX, or PowerPC Altivec).

So for example, if I have something like:

int main() { 
    char x;
    int y;
}

My usual expectation would be to see either 3 or 7 unused bytes between x and y (3 bytes of padding if sizeof(int) == 4, 7 bytes if sizeof(int) == 8). That's not strictly required, but so common that it would be somewhat surprising if it didn't happen.

But regardless of the exact reasoning, the compiler is allowed to insert padding between local variables as it sees fit. In some cases (especially for debugging) they can/will insert extra space and fill it with known values. Then during exit from the routine, they'll check that those values remain intact, to give reasonable assurance that your code hasn't written to memory it shouldn't.

Upvotes: 1

Related Questions