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

时间:2019-01-31 04:29:54

标签: 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;
}

如果我在最后一行中断,并进行调试/检查,这就是我得到的

(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

为什么仅需要0xbffff04b来存储字符时,为什么为char c保留了0xbffff048、0xbffff049、0xbffff04a和0xbffff04b?

这个符号"3\005"意味着什么?

另一方面,如果我的方法如下所示,则该字符没有填充空间,且具有三个额外的存储空间

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

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

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

还不确定为什么在内存保留中将d悬挂在line上方。我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?

1 个答案:

答案 0 :(得分:9)

这称为alignment。对象与特定整数的倍数对齐(对于long,通常为4或8)以便快速访问。 在一般情况下,你不需要过多担心在C ++中的位置,因为语言规范通常使编译器来选择最有效的(在优化的取向而言)的方式来存储对象,通常的情况下

  

每个对象类型具有被称为属性的对齐要求,它是一个整数值(类型的std::size_t,总是一个的功率2)表示字节之间的数可以分配此类型对象的连续地址。的类型的对齐要求可以与被查询alignofstd::alignment_of。指针对齐功能std::align可用于在某个缓冲区中获取适当对齐的指针,而std::aligned_storage可用于获取适当对齐的存储。

     

每个对象类型强加该类型的每一个对象在其对齐要求;更严格的对准(具有较大的对准要求)可以使用请求的alignas

     

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

     

cppreference


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

  

因为cchar&cchar *,所以gdb将其打印为字符串。字符串的第一个字符为'3',即c的值。下一个字符是a的低字节5,gdb以八进制转义符号打印。 Escape sequences in C on Wikipedia - PRL 1024分钟前


为什么在char之后声明char时,便笺簿消失了?因为在这种情况下,char的对准似乎是1,这意味着不需要填充。另一方面,long看起来是4,因此必须有一个4字节的空间,其中放置char

  

我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?

不是。无论是一个变量初始化(一般)不影响它的位置,只知道它有一个不确定的值。另一方面,编译器可以按照自己喜欢的方式随意将对象放置在内存中。在实践中,编译器“享受”可在内存和时间上提高效率的实现。