C 数据对齐:在哪里可以读取?可以改变吗?
这是一本关于内存中基本类型的数据对齐的书 例如,Microsoft Windows对任何K字节的基本对象都提出了更严格的对齐要求 K=2、4或8,必须具有K的倍数的地址。特别是,它要求 双倍的或长的是8的倍数。这一要求提高了服务器端的内存性能 浪费空间的费用。Linux约定,其中8字节值与4字节值对齐 在内存不足、内存接口不可用的时候,边界可能对i386有好处 只有4个字节宽。对于现代处理器,微软的调整是一个更好的设计决策。数据类型 long double,gcc为其生成分配12字节的IA32代码(即使实际数据类型为 仅需要10个字节)对Windows和Linux都有4个字节的对齐要求 问题是:C 数据对齐:在哪里可以读取?可以改变吗?,c,optimization,memory,C,Optimization,Memory,这是一本关于内存中基本类型的数据对齐的书 例如,Microsoft Windows对任何K字节的基本对象都提出了更严格的对齐要求 K=2、4或8,必须具有K的倍数的地址。特别是,它要求 双倍的或长的是8的倍数。这一要求提高了服务器端的内存性能 浪费空间的费用。Linux约定,其中8字节值与4字节值对齐 在内存不足、内存接口不可用的时候,边界可能对i386有好处 只有4个字节宽。对于现代处理器,微软的调整是一个更好的设计决策。数据类型 long double,gcc为其生成分配12字节的IA32代
一般来说,编译器强制执行对齐。无论何时声明基元类型(例如,
double
),编译器都会自动将其与堆栈上的8个字节对齐
此外,内存分配通常也与最大的基元类型对齐,因此您可以安全地执行以下操作:
double *ptr = (double*)malloc(size);
不必担心对齐问题
因此,一般来说,如果你的编程习惯很好,你就不必担心对齐问题。使某个对象不对齐的一种方法是执行以下操作:
char *ch_ptr = (char*)malloc(size);
double *d_ptr = (double*)(ch_ptr + 1);
有一些例外情况:当您开始使用SSE和矢量化时,事情会变得有点混乱,因为
malloc
不再保证16字节对齐
要覆盖某个对象的对齐,MSVC有一个允许此操作的修改器。它用来增加某些东西的对齐度。虽然我不确定它是否可以减少基本类型的对齐。它明确表示不能使用此修改器减少对齐
编辑: 我在GCC上找到了说明
malloc()
对齐的文档:
GNU系统中malloc或realloc返回的块地址
始终是8的倍数(在64位系统上为16)
资料来源:
是的,GCC现在至少可以对齐8个字节。x86 CPU的对齐要求非常宽松。大多数数据可以在未对齐的位置存储和访问,这可能会降低性能。当您开始开发多处理器软件时,事情变得更加复杂,因为对齐对于原子性和观察到的事件顺序变得非常重要(从内存中写入,这可能不完全正确) 编译器通常可以被指示以不同于默认对齐方式的方式对齐变量。有编译器选项和特定于编译器的特殊关键字(例如
#pragma pack
和其他)
既不能由应用程序程序员(操作系统已经编译)也不能由操作系统开发人员(当然,除非他们同意破坏兼容性)更改完善的操作系统API
因此,您可以更改某些内容,但不能更改所有内容。我不知道microsoft从何处获得信息,但结果是 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;
}
这可能是FUD(来自实际设法将未对齐的INT放入MBR的公司…)。但这也可能是由于作者没有得到很好的信息
回答这个问题:是硬件施加了对齐限制。编译器只需要实现它们。谢谢。Linux性能如何?如果已知与4的倍数对齐不再是最佳的,那么我可以改变它吗?据我所知,Linux上最新版本的GCC也与原语类型的大小对齐。因此,我认为旧的4字节对齐方式基本上已经过时了。Windows API使用或产生了大量的结构,如果使用不正确的打包/对齐版本调用这些API,那就太糟糕了。很有趣,因为这本书是2010年出版的。这些东西是否记录在GCC文档中,或者通常不需要对它们进行smth。我不知道是否有记录。但无论如何,GCC(静默地)进行对齐是非常有意义的。在我尝试过的机器上,对
double
的未对齐访问的性能损失是巨大的。(请注意,我实际上没有查看任何GCC生成的程序集来确认8字节对齐,但我从未注意到Windows与Linux之间的性能下降,因此我假设没有)。请指定可以使用#pragma pack
的编译器。Borland,Microsoft,Open Watcom和GNU C/C++编译器支持#pragma pack。您还可以使用gcc选项-mstrict align-mno strict align来控制其行为。如果说更多的是处理器建议(或在某些情况下/体系结构强制)良好的数据对齐,编译器只是努力尊重它(我要说的是,操作系统的参与只是因为它是程序所用数据结构的消费者/生产者)。结论:烧掉这本书。这是胡说八道。引用的文本是胡说八道。两种平台在x86(32位)上都使用不超过4字节的对齐方式x86_64上的double
和long
的8字节对齐。如果您使用的是一些需要特定对齐/打包的固定API(例如,一些使用或生成结构的Windows API函数),您不能使用任意对齐/打包。因此,问题不仅在于硬件。这是windows的问题。unix/linux的方式是:用户代码和系统代码(以及API)共享相同的对齐要求。(好像它们是由同一个compi编译的。)
./a.out
lll1=16, lll2=32
lld=32, lld2=64