目錄
01 什麼是字節對齊?
02 對齊準則又是什麼?
03 爲什麼要字節對齊呢?
04 字節對齊對我們編程有什麼啓示?
目的
- 瞭解字節對齊,可以知道如何計算類/結構體的大小。
01 什麼是字節對齊?
- 計算機中內存大小的基本單位是字節(byte),理論上來講,可以從任意地址訪問某種基本數據類型,但是實際上,計算機並非逐字節大小讀寫內存,而是以2,4,或8的倍數的字節塊來讀寫內存,如此一來就會對基本數據類型的合法地址作出一些限制,即它的地址必須是2,4或8的倍數。那麼就要求各種數據類型按照一定的規則在空間上排列,這就是對齊。
02 對齊準則又是什麼?
- 自對齊:是指該類型成員的其實位置的內存地址必須是自身長度的整數倍。如:int 類型只能以0、4、8…這類的地址開始。
- 程序指定的對齊值:用命令
#pragma pack
時指定的對齊值。 - 自定義類型的自身對齊:結構體或類的成員中自身對齊值是成員最大值。
- 自定義類型的有效對齊值:自定義類型的自身對齊和指定對齊值中取較小的值。
字節對齊有以下準則:
- 結構體變量的首地址能夠被其對齊字節數大小所整除。
- 結構體每個成員相對結構體首地址的偏移都是成員大小的整數倍,如不滿足,對前一個成員填充字節以滿足。
- 結構體的總大小爲結構體對齊字節數大小的整數倍,如不滿足,最後填充字節以滿足。
struct MyStruct
{
char dda; //偏移量爲0,滿足對齊方式,dda佔用1個字節;
double dda1;//下一個可用的地址的偏移量爲1,不是sizeof(double)=8的倍數,需要補足7個字節才能使偏移量變爲8(滿足對齊方式),因此VC自動填充7個字節,dda1存放在偏移量爲8的地址上,它佔用8個字節。
int type; //下一個可用的地址的偏移量爲16,是sizeof(int)=4的倍數,滿足int的對齊方式,所以不需要VC自動填充,type存放在偏移量爲16的地址上,它佔用4個字節。
};
//所有成員變量都分配了空間,空間總的大小爲1+7+8+4=20,不是結構的節邊界數(即結構中佔用最大空間的類型所佔用的字節數sizeof(double)=8)的倍數,所以需要填充4個字節,以滿足結構的大小爲sizeof(double)=8的倍數。所以該結構總的大小爲:sizeof(MyStruc)爲1+7+8+4+4=24。其中總的有7+4=11個字節是VC自動填充的,沒有放任何有意義的東西。
03 爲什麼要字節對齊呢?
- 無論數據是否對齊,大多數計算機還是能夠正確工作。
那麼爲什麼還要進行字節對齊呢?最重要的考慮是提高內存系統性能。
-
需要字節對齊的根本原因在於CPU訪問數據的效率問題。
-
前面我們也說到,計算機每次讀寫一個字節塊,例如,假設計算機總是從內存中取8個字節,如果一個double數據的地址對齊成8的倍數,那麼一個內存操作就可以讀或者寫,但是如果這個double數據的地址沒有對齊,數據就可能被放在兩個8字節塊中,那麼我們可能需要執行兩次內存訪問,才能讀寫完成。顯然在這樣的情況下,是低效的。所以需要字節對齊來提高內存系統性能。
跨平臺通信
- 由於不同平臺對齊方式可能不同,如此一來,同樣的結構在不同的平臺其大小可能不同,在無意識的情況下,互相發送的數據可能出現錯亂,甚至引發嚴重的問題。因此,爲了不同處理器之間能夠正確的處理消息,我們有兩種可選的處理方法。
- 1字節對齊
- 自己對結構進行字節填充
- 我們可以使用僞指令
#pragma pack(n)
(n爲字節對齊數)來使得結構間一字節對齊。
04 字節對齊對我們編程有什麼啓示?
- 雖然我們不需要具體關心字節對齊的細節,但是如果不關注字節對齊的問題,可能會在編程中遇到難以理解或解決的問題。
因此針對字節對齊,總結了以下處理建議:
- 結構體成員合理安排位置,以節省空間。
- 跨平臺數據結構可考慮1字節對齊,節省空間但影響訪問效率。效率之所以降低,是因爲:如果存在更大字節數的變量時(比1大),比如int類型,需要進行多次讀週期才能將一個int數據拼湊起來。
- 跨平臺數據結構人爲進行字節填充,提高訪問效率但不節省空間。
- 本地數據採用默認對齊,以提高訪問效率。
舉例
#include <iostream>
#include <stdio.h>
using namespace std;
int main(){
struct A{
char a;
short b;
int c;
};
cout << "size of struct A = " << sizeof(struct A) << endl;;
struct B{
short b;
int c;
char a;
};
cout << "size of struct B = " << sizeof(struct B) << endl;;
}
輸出: