C++結構體內幕揭秘:sizeof之謎與記憶體佈局探秘

架构师老卢發表於2024-03-23
C++結構體內幕揭秘:sizeof之謎與記憶體佈局探秘

概述:C++結構體的`sizeof`不總是等於每個成員的`sizeof`之和,因為對齊和填充影響了記憶體佈局。未對齊的結構體可能存在間隙,而對齊的結構體會插入填充以保持對齊。透過示例展示了結構體的記憶體對齊和填充,以及如何使用模板超程式設計列印結構體成員的偏移量,深入理解記憶體佈局。

在C++中,結構體的sizeof並不總是等於每個成員的sizeof之和,這是由於對齊和填充的影響。編譯器為了提高記憶體訪問速度,通常會在結構體成員之間插入一些填充位元組以對齊資料。

基礎功能:

示例原始碼:

#include <iostream>

// 未進行對齊的結構體
struct WithoutPadding {
    char a;    // 1 位元組
    int b;     // 4 位元組
    char c;    // 1 位元組
};

// 進行對齊的結構體
struct WithPadding {
    char a;    // 1 位元組
    char padding[3];  // 對齊填充 3 位元組
    int b;     // 4 位元組
    char c;    // 1 位元組
};

int main() {
    std::cout << "WithoutPadding 大小:" << sizeof(WithoutPadding) << std::endl;
    std::cout << "WithPadding 大小:" << sizeof(WithPadding) << std::endl;

    return 0;
}

在這個示例中,WithoutPadding 結構體的大小是 6 位元組(1 + 4 + 1),而WithPadding 結構體的大小是 12 位元組(1 + 3(填充)+ 4 + 1)。這是因為編譯器為了對齊int型別的成員b,在其前面插入了3位元組的填充。

高階功能:

示例原始碼:

#include <iostream>
#include <type_traits>

template <typename T>
void PrintOffsets() {
    std::cout << "Offsets for " << typeid(T).name() << ":" << std::endl;

    size_t offset = 0;
    size_t size = sizeof(T);

    // 使用模板超程式設計逐個列印成員的偏移量
    // 對於 C++17,可以使用 std::is_standard_layout<T> 確保是標準佈局型別
    if constexpr (std::is_standard_layout<T>::value) {
        while (offset < size) {
            std::cout << "  Offset of member at index " << offset << ": " << offsetof(T, offset) << std::endl;
            offset++;
        }
    } else {
        std::cout << "  Not a standard layout type." << std::endl;
    }

    std::cout << std::endl;
}

struct ExampleStruct {
    char a;    // 1 位元組
    int b;     // 4 位元組
    char c;    // 1 位元組
};

int main() {
    PrintOffsets<ExampleStruct>();

    return 0;
}

在這個示例中,PrintOffsets 函式使用模板超程式設計逐個列印結構體成員的偏移量。ExampleStruct 結構體包含了對齊填充,透過offsetof宏可以獲取每個成員的偏移量。這有助於理解結構體記憶體佈局的細節。

透過這兩個示例,展示了結構體大小不等於成員sizeof之和的原因,以及如何使用模板超程式設計逐個列印結構體成員的偏移量。這些知識有助於理解記憶體對齊和結構體記憶體佈局。

相關文章