1. 程式人生 > >關於struct結構體填充造成的位元組數的問題

關於struct結構體填充造成的位元組數的問題

有如下的結構體的定義:

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
};
問題, 求對struct進行sizeof的時候, 得到的大小數。

問題解析: 當記憶體中的值合理對其的時候, 很多機器能夠高效的訪問。 例如, 按位元組定址的32位機器中, 有兩個位元組的short型變數必須放在偶數地址上, 而4位元組的int型變數, 必須放在4的整數倍地址上。 某些機器甚至根本不能訪問沒有對齊的地址。 所以必須要求所有的資料正確的對齊。 

為了解決上述問題, 我們要知道, 32位機器, 對於指標變數, 無論是char型還是int型, 指標變數存的都是地址, 都佔用4bytes。

下面我們一步一步分析:

struct aa{
    char a;
};
上述在記憶體中佔用1個byte。 

|

struct aa{
    char a;
    int b;
};
上述本來佔據5個byte。 但是由於要求int型變數b的地址必須是4的倍數, 所以佔用1(for char) + 3 (填充用的) + 4(for int), 故而佔用8個bytes。
| - - -| | | |
truct aa{
    char a;
    int b;
    float c;
};
上述佔用了12個位元組。 因為已經滿足4的倍數啦。

| - - -  | | | |   | | | | 

struct aa{
    char a;
    int b;
    float c;
    double d;
};

 上述佔用12 + 8個位元組。 但是double要求地址offset從8的整數倍開始, 所以填充4個位元組, 變成16, 變成8的倍數, 在存下double。 是 12 + 4  + 8 = 24 bytes:

(char)| - - - (int) | | | |  (float)| | | | - - - -   (double)| | | | | | | |

下面:

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
};
上述佔用24 + 8 = 32bytes。雖然要char*pa 變數佔用4bytes, 但是 因為要按照double的對其格式。

(char)| - - - (int) | | | |  (float)| | | | - - - -   (double)| | | | | | | | (char*) | | | |  - - - - 

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
};

上述佔用仍然是32 bytes, 因為最後已經有四個空著的bytes, 仍然滿足地址是8的倍數。

(char)| - - - (int) | | | |  (float)| | | | - - - -   (double)| | | | | | | | (char*) | | | |  (int*) | | | |

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
};
上述佔用時32 + 8 = 40 bytes, 因為 short要按照double的格式對其, 即總的地址要是8的倍數。 

(char)| - - - (int) | | | |  (float)| | | | - - - -   (double)| | | | | | | | (char*) | | | |  (int*) | | | | (short) | | - -  - - - -

也就是最後尾部還剩下6個bytes空著了。 試想

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
    int f;
};
由於空6個bytes, 必須是4的倍數, 所把最後四個給佔用了。 所以當然還是40bytes。 當然是。

(char)| - - - (int) | | | |  (float)| | | | - - - -   (double)| | | | | | | | (char*) | | | |  (int*) | | | | (short) | | - -  (int) | | | |

接下來, 如果再加一個short, 就變成了48了, 當然是啦:

struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
    int f;
    short g;
};

編譯器可能提供用於控制結構體的這種填充。 例如#pragma, 這樣我們就可以遮蔽掉變數的對齊方式, 自己設定變數的對齊方式。 

例如編譯器提供#pragma pack(n) 來設定變數以n位元組對齊的方式。 n 位元組的對齊方式指的是, 如果n 大於該變數所佔的位元組數, 那麼偏移量必須滿足預設的對齊方式, 如果n 小於變數型別所佔的位元組數, 那麼偏移量必須為n的倍數。 

舉例子如下:

#include <iostream>

using namespace std;

#pragma pack(push) // 保持對齊方式
#pragma pack(4) // 設定4位元組對齊
struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
};
#pragma pack(pop) // 恢復對齊方式

int main() {

    cout << sizeof(aa) << endl;

    return 0;
}
執行結果如下:


分析如下:

4 + 4 + 4 + 8 + 4 + 4 + 4 = 32.

如果我們設定1位元組對齊:

#include <iostream>

using namespace std;

#pragma pack(push) // 保持對齊方式
#pragma pack(1) // 設定1位元組對齊
struct aa{
    char a;
    int b;
    float c;
    double d;
    char* pa;
    int* pb;
    short e;
};
#pragma pack(pop) // 恢復對齊方式

int main() {

    cout << sizeof(aa) << endl;

    return 0;
}
執行結果為:


分析:

 1 + 4 + 4 + 8 + 4 + 4 + 2 = 27. 

okay。 完美解決。 

為了理解pack, 給出如下例子:

#include <iostream>

using namespace std;

#pragma pack(push, 1) // 可以合併在一起寫
struct Name{
    bool a;
    double c;
};
#pragma pack(pop)
int main() {
    cout << sizeof(Name);
    return 0;
}
執行:


#include <iostream>

using namespace std;

#pragma pack(push, 4)
struct Name{
    bool a;
    double c;
};
#pragma pack(pop)
int main() {
    cout << sizeof(Name);
    return 0;
}
執行:


改為:

#include <iostream>

using namespace std;

#pragma pack(push, 8)
struct Name{
    bool a;
    double c;
};
#pragma pack(pop)
int main() {
    cout << sizeof(Name);
    return 0;
}


執行

再比如下面:

#include <iostream>

using namespace std;

#pragma pack(8)
struct aa {
    uint8_t a;
    uint16_t b;
    uint32_t c;

};
#pragma pack()
int main() {
    cout << sizeof(aa) << endl;
}

佔8個位元組:

|- || ||||

再比如:

<pre class="cpp" name="code">#pragma pack(8)
struct aa {
    uint8_t a;
    uint8_t a2;
    uint16_t b;
    uint32_t c;

};
#pragma pack()

仍佔8個位元組。


在比如:

#pragma pack(8)
struct aa {
    uint8_t a;
    uint16_t b;
    uint32_t c;
    uint8_t a2;

};
#pragma pack()


上述佔12個位元組。 按照預設的對齊方式。

相關推薦

關於struct結構填充造成位元組的問題

有如下的結構體的定義: struct aa{ char a; int b; float c; double d; char* pa; int* pb; short e; };問題, 求對struct進行sizeof的

struct結構實際佔用位元組

原則1.   各成員變數的偏移量必須為該成員大小的整數倍,如不夠則在上一個變數後填充位元組數  struct student { char name;     int id; double score; }; name 偏移量為0,成員大小為1,直接存; id

關於計算結構陣列中位元組

2.結構體計算長度,如下:  #include <stdio.h> #include <string.h> struct student {   int num;   char name[20];   char sex;   float score;

C語言中free()函釋放struct結構中的規律

void poi inf clu main 圖片 刪除 動態分配 不同 並不是什麽新鮮的事情,不過值得註意。首先我們知道,在使用struct來定義並聲明一個變量時,將會自動劃分出一個連續的儲存空間(雖然根據某些對齊原則會出現內存間隙,但是大體上來說還是連續的)這一塊連續空間

struct結構指定初始化項目

錯位 發生 頻繁 常見 語法 float 其中 標準 硬件 標準C的標記化結構初始化語法在標準C中(C89)結構標準初始化是用{}來實始化,在C99的版本,采用了采用可讀性更強的標記化實始化,這在LINUX內核和驅動很為常見。這是ISOC99的用法CPrimer P

Swift Struct 結構

oot truct 必須 使用 ppp 關鍵字 pri author UC 前言 結構體是值類型,並且只有在賦予變量或常量,或者被函數調用時才被賦值。 1、結構體的創建 結構體基本結構 struct 結構體名 { var 或 let 字段名 = 初始化值

C語言 Struct 結構在 Java 中的體現

修飾符 無法 輸入 更多 好用 ++ [] 的區別 import   大一整個學期完成了 C 語言的學習,大二就進入了Java 的學習。   和C語言一樣,我們都會嘗試寫一個小小的學生管理系統什麽的,學習過 C 語言同學知道,在管理系統中 Struct 結構體是個很好用的東

Go/複合資料型別/struct-結構

# 結構體 package main import "fmt" type Stu struct{ age int sex byte //字元型 name string } func main() { //結構體定義與初始化 //順序初始化 每個成員都必須初始化 var s S

[UE4]自定義結構、類、據表

png table 小地圖 比例 pub 地圖 sse 面板 gpa 自定義數據表: #pragma once #include "CoreMinimal.h" #include "Engine/UserDefinedStruct.h" #include "

c++ 中 extern 對struct 結構的使用

型別的定義和型別變數的定義不同, 型別定義只是描述一個型別, 是給編譯器看的, 不會產生可執行程式碼。 變數定義是指在執行檔案中真實得存在這麼一塊內容。    因為每個.c裡都要寫清楚型別定義很麻煩, 所以一般都把型別定義寫在.h裡, 而在.c裡採

struct 結構“重定義;不同的基型別”

今天遇到這麼一個問題,一個結構體編譯報錯,“重定義;不同的基型別”,這個結構體在一個頭檔案中定義,在兩個cpp檔案中包含該標頭檔案,就報這個錯誤,結構體的定義形式為:     typedefine struct strct{ int x;

C/C++ struct 結構定義 用法詳解

在C語言中,定義一個結構體型別要用typedef : typedef struct point { int x; int y; }Point; 在宣告變數的時候就可以:Point

struct結構佔記憶體大小計算

注意:struct 的{}後面要加上 ”;“ #include<stdio.h> struct A {            int a;           double b;            char c; }; struct B {            double b;   

C#語言struct結構適用場景和注意事項

C#中struct結構體是一個特殊的存在,值型別棧內拷貝。struct和class定義上有些相似,區別主要是值型別和引用型別的區別。Winform中涉及到原生代碼的地方大量使用了struct,這很大程度上是為了程式碼移植的需要,不能作為我們寫程式碼的規範參考。我

C語言 struct結構的變數宣告加冒號

有些資訊在儲存時,並不需要佔用一個完整的位元組,而只需佔幾個或一個二進位制位。例如在存放一個開關量時,只有0和1兩種狀態,用一位二進位即可。 為了節省儲存空間,並使處理簡便,C語言又提供了一種資料結

更改結構的記憶體位元組對齊方式--經典

為什麼要對齊?     現代計算機中記憶體空間都是按照byte劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定型別變數的時候經常在特定的記憶體地址訪問,這就需要各種型別資料按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。     對齊的作用和原因:

VC中struct結構佔用的記憶體空間

為上面的結構分配空間的時候,VC根據成員變量出現的順序和對齊方式,先為第一個成員dda1分配空間,其起始地址跟結構的起始地址相同(剛好偏移量0剛好為sizeof(double)的倍數),該成員變數佔用sizeof(double)=8個位元組;接下來為第二個成員dda分配空間,這時下一個可以分配的地址對於結構的

golang struct結構方法中的引數需要定義為指標型別

前幾日寫一個網頁的簡單計數器問題時發現,計數器居然永遠為0,計數器不計數,見鬼了。。。 程式碼如下: type Counter struct { n int } func (ctr Counter) ServeHTTP(c http.ResponseWriter, r

struct結構的初始化及typedef的理解總結

struct結構體是C語言中非常重要的複合型別,初始化的方法很多,下面對這些方法進行總結,便於以後查閱。 一、gcc擴充套件方式(不知道該如何命名) #include <stdio.h> struct mych {       int k;   };

Go語言核心之美 3.4-Struct結構

struct(結構體)也是一種聚合的資料型別,struct可以包含多個任意型別的值,這些值被稱為struct的欄位。用來演示struct的一個經典案例就是僱員資訊,每條僱員資訊包含:員工編號,姓名,住址