这个结构对齐的原因是什么?

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

在字段 flagssurface 之间有一个 8 字节的间隙,即使 flags 的基础类型是 uint32_t。字段 queueFamilyIndexCountpQueueFamilyIndices 也是如此。为什么 flagsqueueFamilyIndexCount 占用 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字节。在这个结构中,cptr 之间将有 7 个字节的填充,这样 offsetof(A, c) == 0offsetof(A, ptr) == 8。并且 alignof(A) == 8。每当创建实例 A a(在堆栈或堆上)时,&a % 8 == 0,因此 &(a.ptr) % 8 == 0


这样做是因为某些 CPU 指令(例如 SIMD 向量指令)要求它们的操作数在字边界处对齐,即内存中 8 的倍数(或 4 对于 32 位)。如果没有正确对齐,这些指令将无法直接使用,程序会变慢。


在此示例中,VkSurfaceKHRpQueueFamilyIndices 是带有 alignof == 8 的指针。