结构的大小和对齐方式
size of struct and alignment
根据此post,struct 的对齐是特定于实现的,这意味着不同的编译器将以不同的方式对齐 struct 中的成员,从而在不同的编译器中给出相同结构的不同大小。
但是,在这个 video 中,演讲者听起来像是以下结构在编译器中的大小必须为 16 和 12:
#include <iostream>
struct C {
uint64_t x;
uint32_t y;
};
struct D {
uint32_t x;
uint32_t y;
uint32_t z;
};
int main() {
std::cout << sizeof(C) << std::endl;
std::cout << sizeof(D) << std::endl;
}
他们确实是 16 岁和 12 岁。
为什么他们必须是 16 岁和 12 岁?不是 16 和 16 吗?
And they are indeed 16 and 12.
他们是这个尺寸,给定:
- 您使用的编译器(和选项)
- 你的目标平台
- 代码中缺少与 aligment/packing 相关的指令
对于你的视频,我想演讲者只是用给定的 platform/toolchain 来发展他的例子。但是一般来说,由于 sizeof(T)
是 compiler/platform 相关的,因此 std::atomic<T>::is_lock_free()
也是 compiler/platform 相关的。
例子
使用以下结构:
struct C {
uint64_t x;
uint32_t y;
};
不同的编译器和选项
- gcc 9.1 "-m32":
sizeof(C) = 12
- msvc x86 19.20:
sizeof(C) = 16
目标平台
- gcc 6.2 for x86-64:
sizeof(C) = 16
- gcc 6.2.1 for MSP430:
sizeof(C) = 12
Alignment/packing 指令
- gcc 9.1 x64 - default:
sizeof(C) = 16
- gcc 9.1 x64 - packed:
sizeof(C) = 12
- gcc 9.1 x64 - align 128:
sizeof(C) = 128
为什么会有这些差异?
编译器可以在 structure/class 的任何字段后自由添加未使用的 bits/bytes。他们这样做是出于性能原因:在某些平台上,read/write a multi-byte int
验证某些对齐属性(通常是 N
字节的地址int
必须能被 N
整除)。
通常,您的 C++ 编译器在您背后执行低级优化很方便。有时您希望对此功能进行更多控制(non-exhaustive 原因列表):
- 当您序列化将由不同程序读取的数据时(保存到文件,发送到网络)。
- 当内存使用比执行速度更重要时。
- 在多核&多线程程序中,控制内核之间CPU cache line can limit cache invalidations个数
struct
,提高执行速度。
这就是编译器通常提供实用程序来控制它的原因。
TL;DR
对于给定的 T
,sizeof(T)
不会 "have" 成为任何东西。它是 compiler/platform 相关的,您通常可以使用特定的编译器指令覆盖它。
根据此post,struct 的对齐是特定于实现的,这意味着不同的编译器将以不同的方式对齐 struct 中的成员,从而在不同的编译器中给出相同结构的不同大小。
但是,在这个 video 中,演讲者听起来像是以下结构在编译器中的大小必须为 16 和 12:
#include <iostream>
struct C {
uint64_t x;
uint32_t y;
};
struct D {
uint32_t x;
uint32_t y;
uint32_t z;
};
int main() {
std::cout << sizeof(C) << std::endl;
std::cout << sizeof(D) << std::endl;
}
他们确实是 16 岁和 12 岁。
为什么他们必须是 16 岁和 12 岁?不是 16 和 16 吗?
And they are indeed 16 and 12.
他们是这个尺寸,给定:
- 您使用的编译器(和选项)
- 你的目标平台
- 代码中缺少与 aligment/packing 相关的指令
对于你的视频,我想演讲者只是用给定的 platform/toolchain 来发展他的例子。但是一般来说,由于 sizeof(T)
是 compiler/platform 相关的,因此 std::atomic<T>::is_lock_free()
也是 compiler/platform 相关的。
例子
使用以下结构:
struct C {
uint64_t x;
uint32_t y;
};
不同的编译器和选项
- gcc 9.1 "-m32":
sizeof(C) = 12
- msvc x86 19.20:
sizeof(C) = 16
目标平台
- gcc 6.2 for x86-64:
sizeof(C) = 16
- gcc 6.2.1 for MSP430:
sizeof(C) = 12
Alignment/packing 指令
- gcc 9.1 x64 - default:
sizeof(C) = 16
- gcc 9.1 x64 - packed:
sizeof(C) = 12
- gcc 9.1 x64 - align 128:
sizeof(C) = 128
为什么会有这些差异?
编译器可以在 structure/class 的任何字段后自由添加未使用的 bits/bytes。他们这样做是出于性能原因:在某些平台上,read/write a multi-byte int
验证某些对齐属性(通常是 N
字节的地址int
必须能被 N
整除)。
通常,您的 C++ 编译器在您背后执行低级优化很方便。有时您希望对此功能进行更多控制(non-exhaustive 原因列表):
- 当您序列化将由不同程序读取的数据时(保存到文件,发送到网络)。
- 当内存使用比执行速度更重要时。
- 在多核&多线程程序中,控制内核之间CPU cache line can limit cache invalidations个数
struct
,提高执行速度。
这就是编译器通常提供实用程序来控制它的原因。
TL;DR
对于给定的 T
,sizeof(T)
不会 "have" 成为任何东西。它是 compiler/platform 相关的,您通常可以使用特定的编译器指令覆盖它。