2

这是一本关于内存中原始类型的数据对齐的书。

Microsoft Windows 提出了更严格的对齐要求——任何 K 字节的原始对象,对于 K = 2、4 或 8,其地址必须是 K 的倍数。特别是,它要求 double 或 long 的地址long 是 8 的倍数。此要求以浪费一些空间为代价来提高内存性能。Linux 约定,其中 8 字节值在 4 字节边界上对齐可能对 i386 有利,当时内存稀缺且内存接口只有 4 字节宽。对于现代处理器,微软的调整是一个更好的设计决策。数据类型 long double,gcc 为其生成分配 12 个字节的 IA32 代码(即使实际数据类型只需要 10 个字节)在 Windows 和 Linux 中都有 4 字节对齐要求。

问题是:

  1. 是什么强加了数据对齐、操作系统或编译器?
  2. 我可以更改它还是修复它?
4

3 回答 3

3

一般来说,强制对齐的是编译器。每当您声明原始类型(例如double)时,编译器会自动将其对齐到堆栈上的 8 个字节。

此外,内存分配通常也与最大的原始类型对齐,因此您可以安全地执行此操作:

double *ptr = (double*)malloc(size);

无需担心对齐。

因此,一般来说,如果您以良好的习惯进行编程,则不必担心对齐问题。使某些东西错位的一种方法是执行以下操作:

char *ch_ptr = (char*)malloc(size);

double *d_ptr = (double*)(ch_ptr + 1);

有一些例外:当你开始进入 SSE 和矢量化时,事情会变得有点混乱,因为malloc不再保证 16 字节对齐。


为了覆盖某些东西的对齐方式,MSVC 具有declspec(align)允许这样做的修饰符。它用于增加某些东西的对齐方式。虽然我不确定它是否可以让你减少原始类型的对齐。它明确表示您不能减少与此修饰符的对齐。


编辑 :

我找到了说明malloc()GCC 对齐的文档:

在 GNU 系统中 malloc 或 realloc 返回的块的地址始终是 8 的倍数(或 64 位系统上的 16)。

来源:http ://www.gnu.org/s/hello/manual/libc/Aligned-Memory-Blocks.html

所以是的,GCC 现在至少对齐 8 个字节。

于 2011-12-15T00:15:04.213 回答
3

x86 CPU 的对齐要求非常宽松。大多数数据可以在未对齐的位置存储和访问,可能以降低性能为代价。当您开始开发多处理器软件时,事情变得更加复杂,因为对齐对于原子性和观察到的事件顺序变得很重要(从内存中写入,这可能不完全正确)。

通常可以指示编译器以不同于默认对齐方式的方式对齐变量。有编译器选项和特殊的编译器特定的关键字(例如#pragma pack和其他)。

应用程序程序员(操作系统已经编译)和操作系统开发人员(当然,除非他们可以破坏兼容性)都无法更改完善的操作系统 API。

所以,你可以改变一些东西,但不能改变一切。

于 2011-12-15T00:21:10.860 回答
0

我不知道微软从哪里得到它的信息,但是 gcc 上的结果(4.6.1 目标:x86_64-linux-gnu,标准模式,除了 -Wall 没有标志)是完全不同的:

#include <stdio.h>

struct lll {
    long l; 
    long long ll;
    };      

struct lld {
    long l; 
    long double ld;
    };      

struct lll lll1, lll2[2];
struct lld lld1, lld2[2];

int main(void)
{   
printf("lll1=%u, lll2=%u\n"
    , (unsigned) sizeof lll1
    , (unsigned) sizeof lll2
    );      

printf("lld=%u, lld2=%u\n"
    , (unsigned) sizeof lld1
    , (unsigned) sizeof lld2
    );      

return 0;
}

结果:

./a.out
lll1=16, lll2=32
lld=32, lld2=64

这可能是 FUD(来自实际上设法将未对齐整数放入 MBR 的公司......)。但这也可能是作者没有被告知太多的结果。

回答这个问题:是硬件施加了对齐限制。编译器只需要实现它们。

于 2011-12-15T00:28:24.390 回答