Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How come local types always get an even address

Tags:

c++

function

call

Given that example code:

void func( char arg)
{
    char a[2];
    char b[3];
    char c[6];
    char d[5];
    char e[8];
    char f[13];
    std::cout << (int)&arg << std::endl;
    std::cout << (int)&a << std::endl;
    std::cout << (int)&b << std::endl;
    std::cout << (int)&c << std::endl;
    std::cout << (int)&d << std::endl;
    std::cout << (int)&e << std::endl;
    std::cout << (int)&f << std::endl;
}

How comes that with every call i get a result similar to that:

3734052
3734048
3734044
3734080
3734088
3734072
3734056

With every address being an even number? And why are the addresses not in the same order like the variables are in the code?

like image 628
VoidStar Avatar asked Jan 02 '14 19:01

VoidStar


2 Answers

And why are the addresses not in the same order like the variables are in the code?

The first element in the structure is guaranteed to be at the same location as the structure itself (if they were members of one), but the other elements are not guaranteed to be in any order. The compiler will order them appropriately to allow it to use the least amount of space, typically. For local variables, where they are located in memory is completely up to the compiler. They could all be in the same area (likely since it would take advantage of locality), or they could be all over the map (if you have a crappy compiler).

With every address being an even number?

It is placing them along word boundaries. This makes memory access faster than if they are not placed on word boundaries. For example, if a were to be placed on the last byte of one word, and the first byte of another:

|             WORD 1                |                WORD 2             |
|--------|--------|--------|--------|--------|--------|--------|--------|
                           |  a[0]  |  a[1]  |

Then accessing a[0] and then a[1] would require loading 2 words into the cache (on a cache miss for each). By placing a along a word boundary:

|             WORD 1                |
|--------|--------|--------|--------|
|  a[0]  |  a[1]  |

A cache miss on a[0] would result in both a[0] and a[1] being loading at the same time (decreasing the unnecessary memory bandwidth). This takes advantage of the principle of locality. While it is certainly not required by the language, it is a very common optimization done by compilers (unless you use preprocessor directives to prevent it).

In your example (shown in their order):

3734044 b[0]
3734045 b[1]
3734046 b[2]
3734047 -----
3734048 a[0]
3734049 a[1]
3734050 -----
3734051 -----
3734052 arg
3734053 -----
3734054 -----
3734055 -----
3734056 f[0]
3734057 f[1]
3734058 f[2]
3734059 f[3]
3734060 f[4]
3734061 f[5]
3734062 f[6]
3734063 f[7]
3734064 f[8]
3734065 f[9]
3734066 f[10]
3734067 f[11]
3734068 f[12]
3734069 -----
3734070 -----
3734071 -----
3734072 e[0]
3734073 e[1]
3734074 e[2]
3734075 e[3]
3734076 e[4]
3734077 e[5]
3734078 e[6]
3734079 e[7]
3734080 c[0]
3734081 c[1]
3734082 c[2]
3734083 c[3]
3734084 c[4]
3734085 c[5]
3734086 -----
3734087 -----
3734088 d[0]
3734089 d[1]
3734090 d[2]
3734091 d[3]
3734092 d[4]

Assuming no other data is being assigned to those holes, it would appear whatever settings you have with your compiler is making sure that all of your arrays start on a word boundary. It isn't that it is adding space between the arrays (as you can see there is no space between e and c), but that the first element must be on a word boundary. This is implementation specific and not at all required by the standard.

like image 58
Zac Howland Avatar answered Nov 15 '22 16:11

Zac Howland


In general, this has nothing to do with the scope of the variables. It has to do with the processor.

A processor that has a word size of 16 bits, likes to fetch it's variables from even addresses. Some 16-bit processors only fetch from even addresses. So, fetching from an odd address will require two fetches, thus doubling the number of memory accesses and slowing down programs.

Other processors may have broader requirements. Many 32-bit processors like to fetch data on 4-byte boundaries. Again, very similar to the 16-bit processor example above.

Some processors do have the ability to fetch data at odd addresses. These are generally 8-bit processors. Some bigger processors cheat and fetch more bytes but ignore all but the requested ones (depending on where the byte lines within the alignment space).

The compiler can allocate local function variables anywhere it chooses, in any order it chooses. The compiler may want to place the frequently accessed variables close together. It may choose not to allocate memory for variables and use registers. It may allocate a variable on the stack, or it may allocate the variable from a completely different memory area. In general, the location of variables doesn't matter as long as the functionality of the program is still correct.

like image 40
Thomas Matthews Avatar answered Nov 15 '22 16:11

Thomas Matthews