这个结构对齐的原因是什么?
What's the reason for this struct's alignment?
在 Vulkan header vulkan.h
中有一个结构定义为
typedef struct VkSwapchainCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkSwapchainCreateFlagsKHR flags;
VkSurfaceKHR surface;
uint32_t minImageCount;
VkFormat imageFormat;
VkColorSpaceKHR imageColorSpace;
VkExtent2D imageExtent;
uint32_t imageArrayLayers;
VkImageUsageFlags imageUsage;
VkSharingMode imageSharingMode;
uint32_t queueFamilyIndexCount;
const uint32_t* pQueueFamilyIndices;
VkSurfaceTransformFlagBitsKHR preTransform;
VkCompositeAlphaFlagBitsKHR compositeAlpha;
VkPresentModeKHR presentMode;
VkBool32 clipped;
VkSwapchainKHR oldSwapchain;
} VkSwapchainCreateInfoKHR;
我使用以下代码查看每个字段的对齐方式 (Visual Studio 2015)
std::cout <<
"sType: " << offsetof(VkSwapchainCreateInfoKHR, sType) << std::endl <<
"pNext: " << offsetof(VkSwapchainCreateInfoKHR, pNext) << std::endl <<
"flags: " << offsetof(VkSwapchainCreateInfoKHR, flags) << std::endl <<
"surface: " << offsetof(VkSwapchainCreateInfoKHR, surface) << std::endl <<
"minImageCount: " << offsetof(VkSwapchainCreateInfoKHR, minImageCount) << std::endl <<
"imageFormat: " << offsetof(VkSwapchainCreateInfoKHR, imageFormat) << std::endl <<
"imageColorSpace: " << offsetof(VkSwapchainCreateInfoKHR, imageColorSpace) << std::endl <<
"imageExtent: " << offsetof(VkSwapchainCreateInfoKHR, imageExtent) << std::endl <<
"imageArrayLayers: " << offsetof(VkSwapchainCreateInfoKHR, imageArrayLayers) << std::endl <<
"imageUsage: " << offsetof(VkSwapchainCreateInfoKHR, imageUsage) << std::endl <<
"imageSharingMode: " << offsetof(VkSwapchainCreateInfoKHR, imageSharingMode) << std::endl <<
"queueFamilyIndexCount: " << offsetof(VkSwapchainCreateInfoKHR, queueFamilyIndexCount) << std::endl <<
"pQueueFamilyIndices: " << offsetof(VkSwapchainCreateInfoKHR, pQueueFamilyIndices) << std::endl <<
"preTransform: " << offsetof(VkSwapchainCreateInfoKHR, preTransform) << std::endl <<
"compositeAlpha: " << offsetof(VkSwapchainCreateInfoKHR, compositeAlpha) << std::endl <<
"presentMode: " << offsetof(VkSwapchainCreateInfoKHR, presentMode) << std::endl <<
"clipped: " << offsetof(VkSwapchainCreateInfoKHR, clipped) << std::endl <<
"oldSwapchain: " << offsetof(VkSwapchainCreateInfoKHR, oldSwapchain) << std::endl <<
std::endl;
并得到了这些结果
sType: 0
pNext: 8
flags: 16
surface: 24
minImageCount: 32
imageFormat: 36
imageColorSpace: 40
imageExtent: 44
imageArrayLayers: 52
imageUsageFlags: 56
imageSharingMode: 60
queueFamilyIndexCount: 64
pQueueFamilyIndices: 72
preTransform: 80
compositeAlpha: 84
presentMode: 88
clipped: 92
oldSwapchain: 96
在字段 flags
和 surface
之间有一个 8 字节的间隙,即使 flags
的基础类型是 uint32_t
。字段 queueFamilyIndexCount
和 pQueueFamilyIndices
也是如此。为什么 flags
和 queueFamilyIndexCount
占用 8 个字节,而它们只有 4 个字节宽,而 uint32_t
类型的每个其他字段只占用 4 个字节?这些偏移量的内存对齐要求有什么特别之处吗?
VkSurfaceKHR
是不可分派的句柄。根据 Vulkan 的定义,它是一个 64 位整数。因此它必须有 8 字节对齐。
布局结构时,编译器将确保每个成员都获得类型所需的对齐方式。如果 surface
紧跟在 flags
之后,则不会有 8 字节对齐。因此,编译器在两者之间插入 4 个字节的填充。
所有类型都有大小 sizeof(T)
和对齐要求 alignof(T)
。它的实例总是需要在内存中放置在 alignof(T)
.
的倍数的地址处
在结构中,编译器会自动在元素之间插入 padding,以便满足其所有元素的要求,相对于结构的起始地址(即对于offsetof
个值)。后续结构元素之间为空 space。
它还设置了整个结构的 alignof
,这样它的所有元素都会在内存中正确对齐。例如在典型的 64 位平台上:
struct A {
char c; // sizeof(char) == 1; alignof(char) == 1
int* ptr; // sizeof(char) == 8; alignof(char) == 8
}
指针的大小为8字节(64位),对齐要求也为8字节。在这个结构中,c
和 ptr
之间将有 7 个字节的填充,这样 offsetof(A, c) == 0
和 offsetof(A, ptr) == 8
。并且 alignof(A) == 8
。每当创建实例 A a
(在堆栈或堆上)时,&a % 8 == 0
,因此 &(a.ptr) % 8 == 0
。
这样做是因为某些 CPU 指令(例如 SIMD 向量指令)要求它们的操作数在字边界处对齐,即内存中 8 的倍数(或 4 对于 32 位)。如果没有正确对齐,这些指令将无法直接使用,程序会变慢。
在此示例中,VkSurfaceKHR
和 pQueueFamilyIndices
是带有 alignof == 8
的指针。
在 Vulkan header vulkan.h
中有一个结构定义为
typedef struct VkSwapchainCreateInfoKHR {
VkStructureType sType;
const void* pNext;
VkSwapchainCreateFlagsKHR flags;
VkSurfaceKHR surface;
uint32_t minImageCount;
VkFormat imageFormat;
VkColorSpaceKHR imageColorSpace;
VkExtent2D imageExtent;
uint32_t imageArrayLayers;
VkImageUsageFlags imageUsage;
VkSharingMode imageSharingMode;
uint32_t queueFamilyIndexCount;
const uint32_t* pQueueFamilyIndices;
VkSurfaceTransformFlagBitsKHR preTransform;
VkCompositeAlphaFlagBitsKHR compositeAlpha;
VkPresentModeKHR presentMode;
VkBool32 clipped;
VkSwapchainKHR oldSwapchain;
} VkSwapchainCreateInfoKHR;
我使用以下代码查看每个字段的对齐方式 (Visual Studio 2015)
std::cout <<
"sType: " << offsetof(VkSwapchainCreateInfoKHR, sType) << std::endl <<
"pNext: " << offsetof(VkSwapchainCreateInfoKHR, pNext) << std::endl <<
"flags: " << offsetof(VkSwapchainCreateInfoKHR, flags) << std::endl <<
"surface: " << offsetof(VkSwapchainCreateInfoKHR, surface) << std::endl <<
"minImageCount: " << offsetof(VkSwapchainCreateInfoKHR, minImageCount) << std::endl <<
"imageFormat: " << offsetof(VkSwapchainCreateInfoKHR, imageFormat) << std::endl <<
"imageColorSpace: " << offsetof(VkSwapchainCreateInfoKHR, imageColorSpace) << std::endl <<
"imageExtent: " << offsetof(VkSwapchainCreateInfoKHR, imageExtent) << std::endl <<
"imageArrayLayers: " << offsetof(VkSwapchainCreateInfoKHR, imageArrayLayers) << std::endl <<
"imageUsage: " << offsetof(VkSwapchainCreateInfoKHR, imageUsage) << std::endl <<
"imageSharingMode: " << offsetof(VkSwapchainCreateInfoKHR, imageSharingMode) << std::endl <<
"queueFamilyIndexCount: " << offsetof(VkSwapchainCreateInfoKHR, queueFamilyIndexCount) << std::endl <<
"pQueueFamilyIndices: " << offsetof(VkSwapchainCreateInfoKHR, pQueueFamilyIndices) << std::endl <<
"preTransform: " << offsetof(VkSwapchainCreateInfoKHR, preTransform) << std::endl <<
"compositeAlpha: " << offsetof(VkSwapchainCreateInfoKHR, compositeAlpha) << std::endl <<
"presentMode: " << offsetof(VkSwapchainCreateInfoKHR, presentMode) << std::endl <<
"clipped: " << offsetof(VkSwapchainCreateInfoKHR, clipped) << std::endl <<
"oldSwapchain: " << offsetof(VkSwapchainCreateInfoKHR, oldSwapchain) << std::endl <<
std::endl;
并得到了这些结果
sType: 0
pNext: 8
flags: 16
surface: 24
minImageCount: 32
imageFormat: 36
imageColorSpace: 40
imageExtent: 44
imageArrayLayers: 52
imageUsageFlags: 56
imageSharingMode: 60
queueFamilyIndexCount: 64
pQueueFamilyIndices: 72
preTransform: 80
compositeAlpha: 84
presentMode: 88
clipped: 92
oldSwapchain: 96
在字段 flags
和 surface
之间有一个 8 字节的间隙,即使 flags
的基础类型是 uint32_t
。字段 queueFamilyIndexCount
和 pQueueFamilyIndices
也是如此。为什么 flags
和 queueFamilyIndexCount
占用 8 个字节,而它们只有 4 个字节宽,而 uint32_t
类型的每个其他字段只占用 4 个字节?这些偏移量的内存对齐要求有什么特别之处吗?
VkSurfaceKHR
是不可分派的句柄。根据 Vulkan 的定义,它是一个 64 位整数。因此它必须有 8 字节对齐。
布局结构时,编译器将确保每个成员都获得类型所需的对齐方式。如果 surface
紧跟在 flags
之后,则不会有 8 字节对齐。因此,编译器在两者之间插入 4 个字节的填充。
所有类型都有大小 sizeof(T)
和对齐要求 alignof(T)
。它的实例总是需要在内存中放置在 alignof(T)
.
在结构中,编译器会自动在元素之间插入 padding,以便满足其所有元素的要求,相对于结构的起始地址(即对于offsetof
个值)。后续结构元素之间为空 space。
它还设置了整个结构的 alignof
,这样它的所有元素都会在内存中正确对齐。例如在典型的 64 位平台上:
struct A {
char c; // sizeof(char) == 1; alignof(char) == 1
int* ptr; // sizeof(char) == 8; alignof(char) == 8
}
指针的大小为8字节(64位),对齐要求也为8字节。在这个结构中,c
和 ptr
之间将有 7 个字节的填充,这样 offsetof(A, c) == 0
和 offsetof(A, ptr) == 8
。并且 alignof(A) == 8
。每当创建实例 A a
(在堆栈或堆上)时,&a % 8 == 0
,因此 &(a.ptr) % 8 == 0
。
这样做是因为某些 CPU 指令(例如 SIMD 向量指令)要求它们的操作数在字边界处对齐,即内存中 8 的倍数(或 4 对于 32 位)。如果没有正确对齐,这些指令将无法直接使用,程序会变慢。
在此示例中,VkSurfaceKHR
和 pQueueFamilyIndices
是带有 alignof == 8
的指针。