我在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
上方。我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?
答案 0 :(得分:9)
这称为alignment。对象与特定整数的倍数对齐(对于long
,通常为4或8)以便快速访问。
在一般情况下,你不需要过多担心在C ++中的位置,因为语言规范通常使编译器来选择最有效的(在优化的取向而言)的方式来存储对象,通常的情况下
每个对象类型具有被称为属性的对齐要求,它是一个整数值(类型的
std::size_t
,总是一个的功率2
)表示字节之间的数可以分配此类型对象的连续地址。的类型的对齐要求可以与被查询alignof
或std::alignment_of
。指针对齐功能std::align
可用于在某个缓冲区中获取适当对齐的指针,而std::aligned_storage
可用于获取适当对齐的存储。每个对象类型强加该类型的每一个对象在其对齐要求;更严格的对准(具有较大的对准要求)可以使用请求的
alignas
。为了满足一个类的所有非静态成员的对齐要求,可以在其某些成员之后插入 padding 。
关于第二个问题,@prl给出的答案:
因为
c
是char
,&c
是char *
,所以gdb将其打印为字符串。字符串的第一个字符为'3',即c
的值。下一个字符是a
的低字节5,gdb以八进制转义符号打印。 Escape sequences in C on Wikipedia - PRL 1024分钟前
为什么在char
之后声明char
时,便笺簿消失了?因为在这种情况下,char
的对准似乎是1,这意味着不需要填充。另一方面,long
看起来是4,因此必须有一个4字节的空间,其中放置char
。
我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?
不是。无论是一个变量初始化(一般)不影响它的位置,只知道它有一个不确定的值。另一方面,编译器可以按照自己喜欢的方式随意将对象放置在内存中。在实践中,编译器“享受”可在内存和时间上提高效率的实现。