C++裡的對齊規則
什麼是對齊?
在C/C++中,資料結構或類的成員變數,並不是按照它們的大小,一個一個緊湊地排列在空間上的。它們是按照一種特定的方法排列的,有可能在兩個成員變數之間插入一個或個byte,以保證每個成員變數的起始位置是都是從某些特定的位置開始的。這就是對齊。單純從語言上描述對齊有些枯燥難以理解,下文將配以例子說明C/C++是基於什麼樣的規則對齊的。
為什麼要對齊?
在大多數平臺上,系統從某些特定的位置開始讀資料非常快,而從其它位置讀資料會慢很多。C/C++是一種注重效率的語言,為了使程式速度儘可能地快,選擇犧牲很少的空間,用填充byte的方式保證所有資料的儲存都從這些特定的位置開始,而達到較高的執行速度。
什麼是4位元組對齊?
當說到一個結構體n位元組對齊,包含2個資訊:
(1)結構體的起始地址能被n整除
(2)結構體的總大小能被n整除
當說到一個成員變數是n位元組對齊的,說明該變數的起始地址能被n整除。比如某變數的對齊位元組數是4,那麼它的首地址的16進位制形式一定是以0/4/8/C結尾的。
在實際場景中,n可以是1、2、4、8
規則開始啦。
1.在64位系統中,預設的對齊位元組是8。在32位系統中,網上查到的資料有說4也有說8,因為用的是64位系統,沒有驗證過32位的情況。
struct type
{
int a;
long b;
};
int main()
{
type t;
cout <<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
return 0;
}
執行結果:
size = 16
address a = 0x7fffa0b9e9d0
address b = 0x7fffa0b9e9d8
排列方式:
a a a a 0 0 0 0
b b b b b b b b
2.對於整個結構體而言,其整個大小也必須是位元組數的倍數,如果不足,後面補充
struct type
{
long a;
int b;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
return 0;
}
執行結果:
size = 16
address a = 0x7ffff8bb0840
address b = 0x7ffff8bb0848
排列方式:
a a a a a a a a
b b b b 0 0 0 0
3.可以通過#pragma pack修改預設值,但只能改小,不能改大。
#pragma pack (4)
struct type
{
long a;
int b;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
return 0;
}
執行結果:
size = 12
address a = 0x7fffe365d4d0
address b = 0x7fffe365d4d8
排列方式:
a a a a
a a a a
b b b b
4.
對於結構體或類中的某一個成員變數而言,它的對齊按照 #pragma pack指定的數值和這個資料成員自身長度中,比較小的那個進行。
a=資料成員自動長度
b=預設值,取值範圍為1,2,4,8
該資料成員對齊位元組=min(a,b)
struct type
{
bool a;
int b;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
return 0;
}
執行結果:
size = 8
address a = 0x7fff43062ed0
address b = 0x7fff43062ed4
排列方式:
a 0 0 0
b b b b
5.
對於一個結構體,它的對齊位元組取取決於結構中最大的成員與#pragma pack指定的數值
a=結構中最大的資料成員的自動長度
b=預設值,取值範圍為1,2,4,8
該結構體對齊位元組=min(a,b)
struct type
{
int a;
bool b;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
return 0;
}
執行結果:
size = 8
address a = 0x7fff518d64a0
address b = 0x7fff518d64a4
排列方式:
a a a a
b 0 0 0
6.
預設值等於或超過所有資料成員長度的時候,預設值的大小將不產生任何效果。
a=max(所有資料成員長度)
b=預設值,取值範圍為1,2,4,8
If(b>a)
該結構體對齊位元組數為a
7.結構體包含結構體的情況
若某一成員變數是另一個結構體,該成員變數的對齊位元組數為成員變數結構體的對齊位元組數
例1:
struct byte3
{
char a;
char b;
char c;
};
struct type
{
byte3 a;
byte3 b;
byte3 c;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
cout<<"address c = "<<&t.c<<endl;
return 0;
}
執行結果
size = 9
address a = 0x7fff6346cdf0
address b = 0x7fff6346cdf3
address c = 0x7fff6346cdf6
說明:
由於所有結構體中最大的位元組長度是1,所以是1位元組對齊的。雖然有一個結構體的大小是3,但是還是按照1對齊的。
例2:
struct byte3
{
short a;
char b;
};
struct type
{
byte3 a;
byte3 b;
byte3 c;
};
int main()
{
type t;
cout<<"size = "<<sizeof(t)<<endl;
cout<<"address a = "<<&t.a<<endl;
cout<<"address b = "<<&t.b<<endl;
cout<<"address c = "<<&t.c<<endl;
return 0;
}
執行結果:
size = 12
address a = 0x7fffff3f1ef0
address b = 0x7fffff3f1ef4
address c = 0x7fffff3f1ef8
說明:
由於所有結構體中最大的位元組長度是1,所以結構體之間是2位元組對齊的。