作者:小爬虫 | 来源:互联网 | 2022-10-26 16:58
我想知道编译器是否会在32位和64位系统上使用不同的填充,因此我在一个简单的VS2019 C ++控制台项目中编写了以下代码:
struct Z
{
char s;
__int64 i;
};
int main()
{
std::cout <
我对每个“平台”设置的期望:
x86: 12
X64: 16
实际结果:
x86: 16
X64: 16
由于x86上的存储字大小为4个字节,因此这意味着它必须以i
两个不同的字存储字节。所以我认为编译器将以这种方式进行填充:
struct Z
{
char s;
char _pad[3];
__int64 i;
};
所以我可以知道背后的原因是什么?
是否与64位系统具有前向兼容性?
由于在32位处理器上支持64位数字的限制?
ComicSansMS..
12
填充不是由字长决定的,而是由每种数据类型的对齐方式决定的。
在大多数情况下,对齐要求等于类型的大小。因此,对于像这样的64位类型,int64
您将获得8字节(64位)对齐。需要将填充插入结构中,以确保该类型的存储最终位于正确对齐的地址处。
当使用两种架构上具有不同大小的内置数据类型(例如指针类型(int*
))时,您可能会看到32位和64位之间的填充差异。
1> ComicSansMS..:
填充不是由字长决定的,而是由每种数据类型的对齐方式决定的。
在大多数情况下,对齐要求等于类型的大小。因此,对于像这样的64位类型,int64
您将获得8字节(64位)对齐。需要将填充插入结构中,以确保该类型的存储最终位于正确对齐的地址处。
当使用两种架构上具有不同大小的内置数据类型(例如指针类型(int*
))时,您可能会看到32位和64位之间的填充差异。
但是,默认对齐方式由字大小确定。最好的理由是单词在内存中存在,以便它们完全适合寄存器。在x86(_64)上,未对齐的数据需要移位操作才能使用。在其他平台上,例如sun sparc,未对齐的数据将导致总线异常。如果要删除填充,请尝试在结构定义中添加__attribute __((packed))`(GCC)。
而且这不是C / C ++语言行为,而是编译器行为
@Nefrin:x86-64具有有效的未对齐负载,可以处理所需的硬件移位。是的,对于大于整数寄存器的类型,仅在某些ABI(例如i386 System V和32位Windows)中仅与寄存器宽度对齐是正常的。但是对于x86-64系统V,`alignof(__ int128)= 16`,以便可以使用SSE向量进行复制,或者用于&#39;lock cmpxchg16b&#39;。但是就C ++标准而言,这完全取决于实现。并且PW的答案表明,对于MSVC来说,结构打包规则可以与仅基于“ alignof(member)”所期望的不同。
@ComicSansMS:在32位MSVC中,“ alignof(int64_t)== 8”,但实际上并没有为确保堆栈上的本地变量而烦恼,因此,这实际上并不是任何“ int64_t”对象所需的最小对齐方式。如果您使用`alignas(8)int64_t tmp;`,则会得到额外的指令来对齐仅用`int64_t tmp`所无法获得的堆栈指针。https://godbolt.org/z/lsuXAQ。正如@PW的答案所示,相对于结构的开始,结构填充规则比仅填充`alignof(T)`更复杂。
2> P.W..:
这是对数据类型的对齐要求的问题,如结构成员的填充和对齐中所指定
每个数据对象都有一个对齐要求。除结构,并集和数组以外的所有数据的对齐要求是对象的大小或当前的包装大小(由/Zp
或包编译指示指定,以较小者为准)。
并且在/ Zp(结构成员对齐)中指定了结构成员对齐的默认值。
下表描述了可用的包装值:
/自Zp
变量效果
1在1字节边界上压缩结构。与/ Zp相同。
2在2字节边界上打包结构。
在4字节边界上打包4个结构。
8在8字节边界上打包结构(x86,ARM和ARM64的默认设置)。
16在16字节边界上打包结构(x64的默认设置)。
由于x86的默认值为/ Zp8,即8个字节,因此输出为16。
但是,您可以通过/Zp
选项指定其他包装尺寸。
这是一个实时演示,/Zp4
其输出为12而不是16。