为什么堆栈中的函数局部变量之间存在空间?

Stu*_*orn 4 c c++ x86 gdb

我在Ubuntu x86上用gcc编译了一个C程序.这是我打电话的功能main

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

如果我在最后一行打破,并调试/检查这是我得到的

(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
Run Code Online (Sandbox Code Playgroud)

c当只需要0xbffff04b来存储char 时,为什么为char保留0xbffff048,0xbffff049,0xbffff04a和0xbffff04b ?

这个符号"3\005"是什么意思?

另一方面,如果我的方法如下所示,对于具有三个额外字节存储的字符,没有填充

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

这就是这些变量的内存分配方式(跳过地址的前导部分)

a - f04c 
c - f04b 
d - f04a 
line - f041, f042, f043, f044, f045, f046, f047, f048, f049
Run Code Online (Sandbox Code Playgroud)

也不确定为什么在内存预留中d悬挂起来line.我假设因为它没有初始化,它会进入堆栈中的不同区域而不是初始化变量?

L. *_* F. 9

这称为对齐.对象与特定整数的倍数(通常为4或8 long)对齐,以便快速访问.通常,您不需要过多担心C++中的位置,因为语言规范通常使编译器能够选择最有效的(就您的优化方向而言)存储对象的方式,这通常就是这种情况.

每个对象类型都有称为对齐要求的属性,它是一个整数值(类型std::size_t,总是幂2),表示可以分配此类对象的连续地址之间的字节数.可以使用alignof或查询类型的对齐要求std::alignment_of.指针对齐函数std::align可用于在某个缓冲区内获得适当对齐的指针,std::aligned_storage并可用于获得适当对齐的存储.

每种对象类型都对该类型的每个对象强加其对齐要求; 可以使用更严格的对齐(具有更大的对齐要求)alignas.

为了满足类的所有非静态成员的对齐要求,可以在其一些成员之后插入填充.

(cppreference)


关于你的第二个问题,@ prl给出了答案:

因为c是a char,&c是a char *,所以gdb将其打印为字符串.字符串的第一个字符是'3',值是c.下一个字符是5,低字节a,gdb以八进制转义符号打印.在维基百科上的C中转义序列 - prl 1024分钟前


为什么在你声明chars之后垫子会消失char?因为在这种情况下,char对齐似乎是1,这意味着不需要填充.另一方面,它long看起来是4,所以必须有一个4字节的空间,char放置它.

我假设因为它没有初始化,它会进入堆栈中的不同区域而不是初始化变量?

并不是的.变量是否初始化(通常)不会影响其放置,只是它具有不确定的值.另一方面,编译器可以自由地将对象放在内存中.在实践中,编译器"享受"实现,从而在内存和时间方面提高效率.