为什么long int在某些计算机上占用12个字节?


26

在我的机器上编译以下代码后,我发现了一些奇怪的事情:

#include <stdio.h>

int main()
{
    printf("Hello, World!\n");

    int a,b,c,d;

    int e,f,g;

    long int h;

    printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
        &a,&b,&c,&d,&e,&f,&g,&h);

    return 0;
}

结果如下。注意,每个int地址之间有4个字节的差异。但是,在最后一个int和长整数之间有12个字节的差异:

 Hello, World!
 The addresses are:

 da54dcac 
 da54dca8 
 da54dca4 
 da54dca0 
 da54dc9c 
 da54dc98 
 da54dc94 
 da54dc88

3
换句话说inth的源代码。编译器可能会将它放在之前h
ctrl-alt-delor

32
不要使用内存地址之间的差异来确定大小。有一个sizeof功能。printf("size: %d ", sizeof(long));
克里斯·施耐德

10
您仅使用来打印地址的低4字节%x。幸运的是,它可以在您的平台上正常运行,以期望的格式字符串传递指针args unsigned int,但是在许多ABI中,指针和整数的大小不同。用于%p在可移植代码中打印指针。(很容易想象一个系统,您的代码将打印前四个指针的上半部分/下半部分,而不是所有8个指针的下半部分。)
Peter Cordes

5
@ChrisSchneider 打印size_t使用%zu。@yoyo_fun 打印地址使用%p。使用错误的格式说明符会
导致

2
@luu不会散布错误信息。没有一个像样的编译器关心在C中声明变量的顺序。如果关心的话,就没有理由按照您的描述那样去做。
gnasher729

Answers:


81

它不需要占用12个字节,而只占用了8个字节。但是,此平台上8字节长的int 的默认对齐方式是8字节。这样,编译器需要将long int移到可以被8整除的地址。“显而易见的”地址da54dc8c不能被8整除,因此需要12个字节的间隙。

您应该能够对此进行测试。如果在long之前添加另一个int,因此有8个int,则应该发现long int可以对齐而无需移动。现在,它距先前的地址只有8个字节。

值得指出的是,尽管此测试应该可以工作,但您不应该依赖以这种方式组织的变量。允许AC编译器做各种时髦的事情,以尝试使您的程序快速运行,包括对变量进行重新排序(有一些警告)。


3
差异,而不是差距。
Deduplicator

10
“包括重新排序变量”。如果编译器决定您不要同时使用两个变量,则可以自由地部分重叠或完全重叠两个变量……
Roger Lipscombe

8
或者确实是将它们保存在寄存器中而不是堆栈中。
17:30停止危害莫妮卡的

11
@OrangeDog我认为这种情况下的地址不会发生,但是总的来说,您当然是正确的。
亚历克斯

5
@Alex:获取地址时,您会得到一些有趣的东西,包括内存和寄存器。取得地址意味着必须给它一个存储位置,但并不意味着必须实际使用它。如果使用该地址,则将其分配3并将其传递给另一个函数,它可能只是将3写入RDI并调用,而从未将其写入内存。有时在调试器中令人惊讶。
Zan Lynx

9

这是因为您的编译器会在变量之间生成额外的填充,以确保它们在内存中正确对齐。

在大多数现代处理器上,如果值的地址是其大小的倍数,则访问它的效率更高。如果将其放在h第一个可用位置,则其地址将为0xda54dc8c,而不是8的倍数,因此使用效率会降低。编译器对此有所了解,并在最后两个变量之间添加了一些未使用的空间,以确保它发生。


感谢您的解释。您能否指出一些有关其大小倍数的变量访问效率更高的原因的材料?我想知道为什么会这样吗?
yoyo_fun

4
@yoyo_fun,如果您 真的想了解内存,那么有一篇关于此主题的著名文章futuretech.blinkenlights.nl/misc/cpumemory.pdf
Alex

1
@yoyo_fun非常简单。某些内存控制器只能访问处理器位宽度的倍数(例如32位处理器只能直接请求地址0-3、4-7、8-11等)。如果您要求一个不对齐的地址,则处理器必须发出两个存储器请求,然后将数据放入寄存器。因此,回到32位,如果您希望将值存储在地址1,则处理器必须询问地址0-3、4-7,然后从1、2、3和4中获取字节。内存读取浪费。
phyrfox

2
较小的问题,但未对齐的内存访问可能是不可恢复的故障,而不是性能下降。取决于架构。
乔恩·切斯特菲尔德

1
@JonChesterfield-是的。这就是为什么我评论说我的描述适用于大多数现代体系结构(我主要指x86和ARM)的原因。还有其他人的行为方式也不同,但它们却很少见。(有趣的是:ARM 曾经是要求对齐访问的体系结构之一,但是在以后的版本中它们增加了对未对齐访问的自动处理)
Jules

2

您的测试不一定要测试您的想法,因为这种语言不需要将任何这些局部变量的地址相互关联。

您必须将它们作为字段放入结构中,以便能够推断出有关存储分配的信息。

不需要局部变量以任何特定方式彼此共享存储。编译器可以在堆栈中的任何位置插入一个临时变量,例如,可以在这些局部变量中的任何两个之间。

相比之下,不允许在结构中插入临时变量,因此,如果您打印结构域的地址,那么您将比较打算从同一逻辑内存(结构)分配的项目。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.