我在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
c
当只需要0xbffff04b来存储char时,为什么为char保留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
。我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?
这称为对齐。对象与特定整数的倍数对齐(对于,通常为4或8 long
)以进行快速访问。通常,您不需要太担心C ++中的位置,因为语言规范通常使编译器可以选择最有效的方式(就您的优化方向而言)来存储对象,通常是这种情况。
每个对象类型都有一个称为“对齐要求”的属性,该属性是一个整数值(类型
std::size_t
,始终为的幂2
),表示可以在其上分配此类型对象的连续地址之间的字节数。可以使用alignof
或查询类型的对齐要求std::alignment_of
。指针对齐功能std::align
可用于在某个缓冲区中获取适当对齐的指针,std::aligned_storage
并可用于获取适当对齐的存储。每个对象类型都对该类型的每个对象施加其对齐要求;可以使用要求更严格的对齐方式(具有更大的对齐要求)
alignas
。为了满足所有非静态成员类别的对齐要求,可以在其某些成员之后插入填充。
关于第二个问题,@ prl给出答案:
因为
c
是char
,&c
是char *
,所以gdb将其打印为字符串。字符串的第一个字符为'3',值为c
。下一个字符是5的低字节a
,gdb以八进制转义符号打印。Wikipedia上C中的转义序列– prl 1024分钟前
当在char
s之后声明s时,为什么垫消失了char
?因为在这种情况下,char
的对齐方式似乎为1,这意味着不需要填充。另一方面,long
的似乎是4,因此必须在其中char
放置一个4字节的空间。
我假设是因为它没有被初始化,所以它与已初始化变量一起进入堆栈中的不同区域?
并不是的。变量是否已初始化(通常)不会影响其放置,只会影响它具有不确定的值。另一方面,编译器可以按照自己喜欢的方式随意将对象放置在内存中。在实践中,编译器“享受”可在内存和时间上提高效率的实现。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句