天天看點

C語言結構體位元組對齊問題1 前言2 舉例3.其他

C/C++結構體位元組對齊問題

  • 1 前言
  • 2 舉例
  • 3.其他

1 前言

對于結構體位元組對齊問題,我發現我一直有一個誤區,正是這個誤區,給我帶來了很大困擾,今天突然頓悟,在此記錄一下,希望能幫到大家。

2 舉例

#include <stdio.h>
#define field_offset(s,f) (int)(&(((struct s *)0)->f))
struct AD  { int a; char b[13]; double c;};
#pragma pack(push)
#pragma pack(1)
struct A1  { int a; char b[13]; double c;};
#pragma pack(2)
struct A2  { int a; char b[13]; double c;};
#pragma pack(4)
struct A4  { int a; char b[13]; double c;};
#pragma pack(8)
struct A8  { int a; char b[13]; double c;};
#pragma pack(16)
struct A16 { int a; char b[13]; double c;};
#pragma pack(pop)
int main()
{
    printf("AD.a %d\n",field_offset(AD,a));//AD.a 0
    printf("AD.b %d\n",field_offset(AD,b));//AD.b 4
    printf("AD.c %d\n",field_offset(AD,c));//AD.c 24
    printf("AD sizeof %d\n", sizeof(AD));//AD sizeof 32
    printf("\n");
    printf("A1.a %d\n",field_offset(A1,a));//A1.a 0
    printf("A1.b %d\n",field_offset(A1,b));//A1.b 4
    printf("A1.c %d\n",field_offset(A1,c));//A1.c 17
    printf("A1 sizeof %d\n", sizeof(A1));//A1 sizeof 25
    printf("\n");
    printf("A2.a %d\n",field_offset(A2,a));//A2.a 0
    printf("A2.b %d\n",field_offset(A2,b));//A2.b 4
    printf("A2.c %d\n",field_offset(A2,c));//A2.c 18
    printf("A2 sizeof %d\n", sizeof(A2));//A2 sizeof 26
    printf("\n");
    printf("A4.a %d\n",field_offset(A4,a));//A4.a 0
    printf("A4.b %d\n",field_offset(A4,b));//A4.b 4
    printf("A4.c %d\n",field_offset(A4,c));//A4.c 20
    printf("A4 sizeof %d\n", sizeof(A4));//A4 sizeof 28
    printf("\n");
    printf("A8.a %d\n",field_offset(A8,a));//A8.a 0
    printf("A8.b %d\n",field_offset(A8,b));//A8.b 4
    printf("A8.c %d\n",field_offset(A8,c));//A8.c 24
    printf("A8 sizeof %d\n", sizeof(A8));//A8 sizeof 32
    printf("\n");
    printf("A16.a %d\n",field_offset(A16,a));//A16.a 0
    printf("A16.b %d\n",field_offset(A16,b));//A16.b 4
    printf("A16.c %d\n",field_offset(A16,c));//A16.c 24
    printf("A16 sizeof %d\n", sizeof(A16));//A16 sizeof 32
    printf("\n");
    return 0;
}
           

如果你認為的結果和上面注釋中的一樣,那你就沒有必要讀下去了。

在上面的例子中,對于A1,A2, A4的結果我都了解,但是對AD,A8,A16我很不了解。

對于AD,我認為結果應該是:

AD.a 0
AD.b 8
AD.c 24
AD sizeof 32
           

我是這樣想的:AD應該是8位元組對齊的,a是4位元組,b占13位元組,a後面雖然空了4個位元組,但是放不下b,是以應該是a後面補4個位元組,接着放b。b13個位元組,不是8的倍數,是以b後面補3個位元組。接着c占8個位元組。

當然,我上面的想法是錯誤的。可是為什麼會錯呢?

其實,問題出在b的身上。b是一個char型的數組,即類型為char *,但是我們通路的是b的元素,即char,是以,b的自身對齊值為1,而不是13。

之是以這裡出錯,實際上是因為沒有深刻認識到為什麼要位元組對齊。位元組對齊是為了提高CPU通路資料的效率。32位字長的計算機,其CPU一次可以讀寫的資料長度是4個位元組64位計算機則一次可以讀取8個位元組。對于char型數組,隻占據一個位元組,小于計算機一次讀取的位元組數,是以無論其元素放在什麼位址,都可以一次讀取到,便“不需要”位元組對齊。是以,b的元素是可以緊挨着a來放的,是以AD實際空間存放情況應如下圖所示:

C語言結構體位元組對齊問題1 前言2 舉例3.其他

AD成員中自身對齊值最大的為8, 是以AD的自身對齊值是8;A8的指定對齊值和自身對齊值一樣,相當于沒指定,還是8位元組對齊;A16指定對齊值16大于自身對齊值8,是以還是8位元組對齊。是以AD,A8,A16的結果是一樣的。

3.其他

這是一個C++程式:

#define field_offset(s,f) (int)(&(((struct s *)0)->f))
int main() {
    struct alignas(4) Info {
        char a;
        short b;
        char c;
    };
    std::cout << sizeof(Info) << std::endl;
    std::cout << field_offset(Info, a) << std::endl;
    std::cout << field_offset(Info, b) << std::endl;
    std::cout << field_offset(Info, c) << std::endl;

    return 0;
}
           

剛開始我以為應該是下面這樣的:

C語言結構體位元組對齊問題1 前言2 舉例3.其他

是以

sizeof(Info) = 4

但運作結果為:

8
0
2
4
           

可以推斷出,Info的實際存儲情況應該為:

C語言結構體位元組對齊問題1 前言2 舉例3.其他

針對此,我有2個問題:

  1. C++11标準中,alignas不是最小設定8位元組對齊,也就最小是alignas(8),否則設定無效嗎?為什麼還會出現上面的結果呢?
  2. 如果

    alignas(4)

    設定生效,根據第二節的例子中提到的:結構體的有效對齊值是自身對齊值和指定對齊值中較小者。那麼實際存儲情況像下面這樣(

    sizeof(Info)= 6

    )啊:
    C語言結構體位元組對齊問題1 前言2 舉例3.其他
    是因為C和C++标準不一樣嗎?

後續:經過驗證,我得到了答案。

參考連結:

  • 為什麼要位元組對齊:
  • 結構體位元組對齊例子
  • 位元組對齊基本概念和準則
  • 結構體元素偏移量計算

繼續閱讀