Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is there a memory space between my function local variables in the stack?

Tags:

c++

c

x86

gdb

I have a C program compiled with gcc on Ubuntu x86. This is a function I am calling from main

void addme()
{
  long a = 5;
  char c = '3';
  long array[3];
  array[0] = 2;
  array[1] = 4;
  array[2] = 8;
}

If I break at last line, and debug/inspect this is what I get

(gdb) print &a
$5 = (long *) 0xbffff04c
(gdb) print &c
$6 = 0xbffff04b "3\005"
(gdb) print &array
$7 = (long (*)[3]) 0xbffff03c
(gdb) x 0xbffff03c
0xbffff03c:     0x00000002
(gdb) x 0xbffff040
0xbffff040:     0x00000004
(gdb) x 0xbffff044
0xbffff044:     0x00000008
(gdb) x 0xbffff04c
0xbffff04c:     0x00000005

Why is 0xbffff048, 0xbffff049, 0xbffff04a and 0xbffff04b reserved for the char c when only 0xbffff04b is required to store a char?

Also what does this notation "3\005" mean?

On the other hand if my method is as below, there is no padding for the character with three extra bytes of storage

void addme()
{
  long a = 5;
  char c = '3';
  char line[9];
  char d = '4';
}

This is how memory allocation looks like for these variables (skipping the leading part of the address)

a - f04c 
c - f04b 
d - f04a 
line - f041, f042, f043, f044, f045, f046, f047, f048, f049

Also not sure why d was hoisted above line in memory reservation. I assume because it wasn't initialized, it goes to a different region in stack than initialized variables?

like image 926
Stubborn Avatar asked Jan 01 '23 08:01

Stubborn


1 Answers

This is called alignment. Objects are aligned to multiples of specific integers (usually 4 or 8 in case of long) for fast access. In general, you don't need to worry too much about the placement in C++, since the language specification usually enables the compiler to choose the most efficient (in terms of your orientation of optimization) way to store objects, which is usually the case.

Every object type has the property called alignment requirement, which is an integer value (of type std::size_t, always a power of 2) representing the number of bytes between successive addresses at which objects of this type can be allocated. The alignment requirement of a type can be queried with alignof or std::alignment_of. The pointer alignment function std::align can be used to obtain a suitably-aligned pointer within some buffer, and std::aligned_storage can be used to obtain suitably-aligned storage.

Each object type imposes its alignment requirement on every object of that type; stricter alignment (with larger alignment requirement) can be requested using alignas.

In order to satisfy alignment requirements of all non-static members of a class, padding may be inserted after some of its members.

(cppreference)


Regarding your second question, @prl gives the answer:

Because c is a char, &c is a char *, so gdb prints it as a string. The first character of the string is '3', the value of c. The next character is 5, the low byte of a, which gdb prints in octal escape notation. Escape sequences in C on Wikipedia – prl 1024 min ago


Why did the pad disappear when you declare chars after the char? Because in this case, char's alignment appears to be 1, which means no padding is needed. On the other hand, long's appears to be 4, so there has to be a 4-byte space, in which the char is placed.

I assume because it wasn't initialized, it goes to a different region in stack than initialized variables?

Not really. Whether an variable is initialized (in general) does not affect its placement, only that it has an indeterminate value. On the other hand, the compiler is free to place the objects in memory the way it likes. In practice, compilers "enjoy" implementations that lead to efficiency, both in memory and time.

like image 173
L. F. Avatar answered Jan 14 '23 13:01

L. F.