⑴ 下載程序時位元組對齊
一、快速理解
1、內存對齊原則:
第一個成員的首地址為0.
每個成員的首地址是自身大小的整數倍
結構體的總大小,為其成員中所含最大類型的整數倍。
2、什麼是位元組對齊?
在C語言中,結構是一種復合數據類型,其構成元素既可以是基本數據類型(如int、long、float等)的變數,也可以是一些復合數據類型(如數組、結構、聯合等)的數據單元。在結構中,編譯器為結構的每個成員按其自然邊界(alignment)分配空間。各個成員按照它們被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構的地址相同。
為了使CPU能夠對變數進行快速的訪問,變數的起始地址應該具有某些特性,即所謂的」對齊」.
比如4位元組的int型,其起始地址應該位於4位元組的邊界上,即起始地址能夠被4整除.
3、 位元組對齊有什麼作用?
位元組對齊的作用不僅是便於cpu快速訪問。
同時合理的利用位元組對齊可以有效地節省存儲空間。
對於32位機來說,4位元組對齊能夠使cpu訪問速度提高,比如說一個long類型的變數,如果跨越了4位元組邊界存儲,那麼cpu要讀取兩次,這樣效率就低了。
但是在32位機中使用1位元組或者2位元組對齊,反而會使變數訪問速度降低。所以這要考慮處理器類型,另外還得考慮編譯器的類型。在vc中默認是4位元組對齊的,GNU gcc 是默認4位元組對齊。
4、 更改C編譯器的預設位元組對齊方式
在預設情況下,C編譯器為每一個變數或是數據單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊。
使用偽指令#pragma pack(),取消自定義位元組對齊方式。
另外,還有如下的一種方式:
__attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。 ·
attribute ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。
5、 舉例說明
例1
struct test
{
char x1;
short x2;
float x3;
char x4;
};
1
2
3
4
5
6
7
1
2
3
4
5
6
7
由於編譯器默認情況下會對這個struct作自然邊界(有人說「自然對界」我覺得邊界更順口)對齊,結構的第一個成員x1,其偏移地址為0,占據了第1個位元組。第二個成員x2為short類型,其起始地址必須2位元組對界。
因此,編譯器在x2和x1之間填充了一個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然邊界地址上,在它們前面不需要額外的填充位元組。
在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大邊界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。
例2
#pragma pack(1) //讓編譯器對這個結構作1位元組對齊
struct test
{
char x1;
short x2;
float x3;
char x4;
};
#pragma pack() //取消1位元組對齊,恢復為默認4位元組對齊
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
這時候sizeof(struct test)的值為8。
例3
#define GNUC_PACKED __attribute__((packed))
struct PACKED test
{
char x1;
short x2;
float x3;
char x4;
}GNUC_PACKED;
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
這時候sizeof(struct test)的值仍為8。
二、深入理解
①什麼是位元組對齊,為什麼要對齊?
現代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變數的訪問可以從任何地址開始。
但實際情況是在訪問特定類型變數的時候經常在特定的內存地址訪問,這就需要各種類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
對齊的作用和原因:各個硬體平台對存儲空間的處理上有很大的不同。一些平台對某些特定類型的數據只能從某些特定地址開始存取。比如有些架構的CPU在訪問一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下編程必須保證位元組對齊.其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對數據存放進行對齊,會在存取效率上帶來損失。比如有些平台每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀周期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀周期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit數據。顯然在讀取效率上下降很多。
②位元組對齊對程序的影響:
先讓我們看幾個例子吧(32bit,x86環境,gcc編譯器):
設結構體如下定義:
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
現在已知32位機器上各種數據類型的長度如下:
char:1(有符號無符號同)
short:2(有符號無符號同)
int:4(有符號無符號同)
long:4(有符號無符號同)
float:4 double:8
那麼上面兩個結構大小如何呢?
結果是:
sizeof(strcut A)值為8
sizeof(struct B)的值卻是12一、快速理解
1、內存對齊原則:
第一個成員的首地址為0.
每個成員的首地址是自身大小的整數倍
結構體的總大小,為其成員中所含最大類型的整數倍。
2、什麼是位元組對齊?
在C語言中,結構是一種復合數據類型,其構成元素既可以是基本數據類型(如int、long、float等)的變數,也可以是一些復合數據類型(如數組、結構、聯合等)的數據單元。在結構中,編譯器為結構的每個成員按其自然邊界(alignment)分配空間。各個成員按照它們被聲明的順序在內存中順序存儲,第一個成員的地址和整個結構的地址相同。
為了使CPU能夠對變數進行快速的訪問,變數的起始地址應該具有某些特性,即所謂的」對齊」.
比如4位元組的int型,其起始地址應該位於4位元組的邊界上,即起始地址能夠被4整除.
3、 位元組對齊有什麼作用?
位元組對齊的作用不僅是便於cpu快速訪問。
同時合理的利用位元組對齊可以有效地節省存儲空間。
對於32位機來說,4位元組對齊能夠使cpu訪問速度提高,比如說一個long類型的變數,如果跨越了4位元組邊界存儲,那麼cpu要讀取兩次,這樣效率就低了。
但是在32位機中使用1位元組或者2位元組對齊,反而會使變數訪問速度降低。所以這要考慮處理器類型,另外還得考慮編譯器的類型。在vc中默認是4位元組對齊的,GNU gcc 是默認4位元組對齊。
4、 更改C編譯器的預設位元組對齊方式
在預設情況下,C編譯器為每一個變數或是數據單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:
使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊。
使用偽指令#pragma pack(),取消自定義位元組對齊方式。
另外,還有如下的一種方式:
__attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。 ·
attribute ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。
5、 舉例說明
例1
struct test
{
char x1;
short x2;
float x3;
char x4;
};
1
2
3
4
5
6
7
1
2
3
4
5
6
7
由於編譯器默認情況下會對這個struct作自然邊界(有人說「自然對界」我覺得邊界更順口)對齊,結構的第一個成員x1,其偏移地址為0,占據了第1個位元組。第二個成員x2為short類型,其起始地址必須2位元組對界。
因此,編譯器在x2和x1之間填充了一個空位元組。結構的第三個成員x3和第四個成員x4恰好落在其自然邊界地址上,在它們前面不需要額外的填充位元組。
在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大邊界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。
例2
#pragma pack(1) //讓編譯器對這個結構作1位元組對齊
struct test
{
char x1;
short x2;
float x3;
char x4;
};
#pragma pack() //取消1位元組對齊,恢復為默認4位元組對齊
1
2
3
4
5
6
7
8
9
1
2
3
4
5
6
7
8
9
這時候sizeof(struct test)的值為8。
例3
#define GNUC_PACKED __attribute__((packed))
struct PACKED test
{
char x1;
short x2;
float x3;
char x4;
}GNUC_PACKED;
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
這時候sizeof(struct test)的值仍為8。
二、深入理解
①什麼是位元組對齊,為什麼要對齊?
現代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變數的訪問可以從任何地址開始。
但實際情況是在訪問特定類型變數的時候經常在特定的內存地址訪問,這就需要各種類型數據按照一定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
對齊的作用和原因:各個硬體平台對存儲空間的處理上有很大的不同。一些平台對某些特定類型的數據只能從某些特定地址開始存取。比如有些架構的CPU在訪問一個沒有進行對齊的變數的時候會發生錯誤,那麼在這種架構下編程必須保證位元組對齊.其他平台可能沒有這種情況,但是最常見的是如果不按照適合其平台要求對數據存放進行對齊,會在存取效率上帶來損失。比如有些平台每次讀都是從偶地址開始,如果一個int型(假設為32位系統)如果存放在偶地址開始的地方,那麼一個讀周期就可以讀出這32bit,而如果存放在奇地址開始的地方,就需要2個讀周期,並對兩次讀出的結果的高低位元組進行拼湊才能得到該32bit數據。顯然在讀取效率上下降很多。
②位元組對齊對程序的影響:
先讓我們看幾個例子吧(32bit,x86環境,gcc編譯器):
設結構體如下定義:
struct A
{
int a;
char b;
short c;
};
struct B
{
char b;
int a;
short c;
};
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
現在已知32位機器上各種數據類型的長度如下:
char:1(有符號無符號同)
short:2(有符號無符號同)
int:4(有符號無符號同)
long:4(有符號無符號同)
float:4 double:8
那麼上面兩個結構大小如何呢?
結果是:
sizeof(strcut A)值為8
sizeof(struct B)的值卻是12
⑵ 結構體內存對齊
結構體內存對齊
一、結構體對齊的三大原則
1、數據成員對齊規則:結構(struct)(或聯合體(union))的數據成員,第一個數據成員放在offset為0的地方,以後每個數據成員存儲的起始位置要從該成員大小或者成員的子成員大小(只要該成員有子成員,比如說是數組、結構體等)的整數倍開始(比如int為4位元組,則要從4的整數倍地址開始存儲)
2、結構體作為成員:如果一個結構體里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲(struct a里存有struct b,b裡面有char、int 、double等元素,那b應該從8的整數倍開始存儲)
3、收尾工作:結構體的總大小,也就是sizeof的結果,必須是其內部最大成員的整數倍,不足的要補齊。
首先,我們看一下各個數據類型在不同操作系統中所佔的位數
二、內存對齊規則的應用
圖中輸出結果是24,為什麼是24呢?
a 為double類型,佔8個位元組,又因為是第一個成員,起始值對應offset為0的位置。又因為佔8個位元組,所以a對應的是偏移量為8的地址空間
b 為char類型,佔1個位元組,對齊到1的整數倍,也就是下一個地址空間。
c 為int類型,佔4個位元組,對齊到4的整數倍,對齊數為12,偏移量為4的地址空間
d 為short類型,佔2個位元組,對齊到2的整數倍,對齊數為16,偏移量為2的地址空間
結構體的總大小,必須是其內部最大成員的整數倍,不足的要補齊,struct1中最大的成員為double類型,那麼結構體的總大小即為8的整數倍,目前已經佔了17位元組,所以總大小應為24個位元組
我們再看一下下面的輸出又是多少呢?
輸出結果是16。
如圖a不變
b 為int類型,佔4個位元組,對齊到4的整數倍,對齊數為8,偏移量為4的地址空間
c 為char類型,佔1個位元組,對齊到1的整數倍,也就是下一個地址空間。
d 為short類型,佔2個位元組,對齊到2的整數倍,對齊數為14,偏移量為2的地址空間
又因為內存對齊原則,所以結構體的總大小為16個位元組
那麼結構體是否可以嵌套使用呢,嵌套使用的話,內存又該如何計算呢?
這里的輸出結果是多少呢。我們來計算一下
如圖a不變
b 為int類型,佔4個位元組,對齊到4的整數倍,對齊數為8,偏移量為4的地址空間
c 為char類型,佔1個位元組,對齊到1的整數倍,也就是下一個地址空間。
d 為short類型,佔2個位元組,對齊到2的整數倍,對齊數為14,偏移量為2的地址空間
e 為int類型,佔4個位元組,對齊到4的整數倍,對齊數為16,偏移量為4的地址空間
已知struct1佔24個位元組,double 佔8個位元組,不知道為什麼的請往上看。
結構體作為成員:如果一個結構體里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲(struct a里存有struct b,b裡面有char、int 、double等元素,那b應該從8的整數倍開始存儲)
所以struct1 應該從8的整數倍開始存儲
那麼struct1 佔24個位元組,對齊到8的整數倍,對齊數為24,偏移量為24的地址空間
所以結構體的總大小為48個位元組
三、為什麼要內存對齊呢?
(一)性能上的提升
從內存佔用的角度講,對齊後比未對齊有些情況反而增加了內存分配的開支,是為了什麼呢?
數據結構(尤其是棧)應該盡可能地在自然邊界上對齊,為了訪問未對齊的內存,處理器需要作兩次內存訪問;而對齊的內存訪問僅需要一次訪問。最重要的是提高內存系統的性能。
(二)跨平台
有些硬體平台並不能訪問任意地址上的任意數據的,只能處理特定類型的數據,否則會導致硬體層級的錯誤。
有些CPU(如基於 Alpha,IA-64,MIPS,和 SuperH 體系的)拒絕讀取未對齊數據。當一個程序要求這些 CPU 讀取未對齊數據時,這時 CPU 會進入異常處理狀態並且通知程序不能繼續執行。
舉個例子,在 ARM,MIPS,和 SH 硬體平台上,當操作系統被要求存取一個未對齊數據時會默認給應用程序拋出硬體異常。所以,如果編譯器不進行內存對齊,那在很多平台的上的開發將難以進行。
⑶ 有經驗的C語言程序常說的「內存對齊」,原因究竟是什麼
在C語言程序開發中,有時有經驗的程序員會提起「內存對齊」一詞,事實上,這也是C語言中結構體的 size 不等於它所有成員 size 之和的原因(C語言中的結構體的size,並不等於它所有成員size之和,為什麼?),那麼,C語言程序為什麼要「內存對齊」呢?
事實上,本節只是粗淺討論,處理器的內存系統比這里描述的要復雜得多,涉及的內容也要復雜得多。不過,我們至少已經知道,在C語言程序中堅持內存對齊還是有很多好處的。
⑷ 結構體struct {int num; int age;char sex;}佔多少個位元組
說到結構體的內存佔用,就必須要提到內存對齊的一個概念。
32位系統下,我們普遍在用的gcc編譯器和vc編譯器默認是按照4個位元組的大小實現內存對齊的,也就是說結構體的內存分配默認是按照4個位元組的倍數進行分配的。
對於結構體struct
{
int
num;
int
age;
char
sex;
}來說,int
num
佔用4個位元組,int
age佔用4個位元組
char
sex是一個位元組長度,但是需要符合內存對齊原則,因此在內存分配的時候,也是佔用4個位元組的長度,因此整個結構體佔用的內存大小為12個位元組
⑸ linux內存分配默認是多少位元組對齊
VC和GCC默認的都是4位元組對齊,編程中可以使用#pragma pack(n)指定對齊模數。出現以上差異的原因在於,VC和GCC中對於double類型的對齊方式不同。
Win32平台下的微軟VC編譯器在默認情況下採用如下的對齊規則: 任何基本數據類型T的對齊模數就是T的大小,即sizeof(T)。比如對於double類型(8位元組),就要求該類型數據的地址總是8的倍數,而char類型數據(1位元組)則可以從任何一個地址開始。
Linux下的GCC奉行的是另外一套規則:任何2位元組大小(包括單位元組嗎?)的數據類型(比如short)的對齊模數是2,而其它所有超過2位元組的數據類型(比如long,double)都以4為對齊模數。
復雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是復雜類型時,可以最小化長度。
struct{char a;double b;}
在VC中,因為結構中存在double和char,按照最長數據類型對齊,char只佔1B,但是加上後面的double所佔空間超過8B,所以char獨佔8B;而double佔8B,一共16Byte。
在GCC中,double長度超過4位元組,按照4位元組對齊,原理同上,不過char佔4位元組,double占兩個4位元組,一共12Byte。
⑹ 匯編高手來說下內存對齊背後CPU究竟是怎麼操作的
內存對齊:
我們知道現代計算機體系中CPU按照雙字、字、位元組訪問存儲內存,並通過匯流排進行傳輸,若未經一定規則的對齊,CPU的訪址操作與匯流排的傳輸操作將會異常的復雜,所以現代編譯器中都會對內存進行自動的對齊。
1.內存對齊系數
說道內存對齊,就不得不說內存對齊系數, 對齊系數最簡單的設置方法是使用 #pragma pack(n)進行設置,這部分點進鏈接在我的文章內有詳細說明!
2.sizeof
說到內存對齊第二個不得不說的就是sizeof,它的基本作用是判斷數據類型或者表達式長度,要注意的是這不是一個函數,而是一個C++中的關鍵字!位元組數的計算在程序編譯時進行,而不是在程序執行的過程中才計算出來!
3.類型的長度與數據成員對齊
你的計算機中,數據類型的長度指的就是在你的計算機中對數據類型使用sizeof得到的結果,當然這個在各種不同的編譯環境下得到的結果是不同的。
比如在32位Visual Studio環境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 4
cout << sizeof(double) << endl; // 8
而在64位G++編譯環境下:
cout << sizeof(char) << endl; // 1
cout << sizeof(short) << endl; // 2
cout << sizeof(int) << endl; // 4
cout << sizeof(long) << endl; // 8
cout << sizeof(double) << endl; // 8
下面我將在32位Visual Studio環境下講解數據成員對齊:
首先我們要清楚結構體struct中的成員在內存中的分配是連續的,struct內的首地址也就是struct內第一個數據成員的地址,換句話說struct內第一個數據成員離struct開始的距離offset = 0。
數據成員對齊的規則就是,而在第一個成員之後,每個成員距離struct首地址的距離 offset, 都是struct內成員自身長度(sizeof) 與 #pragma pack(n)中的n的最小值的整數倍,如果未經對齊時不滿足這個規則,在對齊時就會在這個成員前填充空子節以使其達到數據成員對齊。
默認n為8時:
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 16
cout << (int *)&myStruct.a << endl; // 0024F898
cout << &myStruct.b << endl; // 0024F8A0(因運行時而異)
當設置n為4也就是min(sizeof(double), n) = 4 時:
#pragma pack(4)
struct {
char a;
double b;
} myStruct;
cout << sizeof myStruct << endl; // 12
cout << (int *)&myStruct.a << endl; // 0046F76C
cout << &myStruct.b << endl; // 0046F770
第一個例子時,最小值為8,填充7個位元組到char a 之後。
第二個例子時,最小值為4,填充3個位元組到char a之後。
4.整體對齊
編譯器在進行過數據成員對齊之後,還要進行整體對齊。與數據對齊相似但不是完全相同, 如果數據對齊完成時struct的大小不是 struct內成員自身長度最大值(sizeof) 與 #pragma pack(n)中的n的最小值的整數倍。(注意這里是成員中長度最大的那個與n比較,而不是特定的一個成員。)就要在struct的最後添加空位元組直到對齊。
當設置n為4也就是min(sizeof(short), n) = 2 時:
#pragma pack(4)
struct {
char a;
short b;
char c;
} myStruct;
cout << sizeof myStruct << endl; // 6
cout << (int *)&myStruct.a << endl; // 003DFED0
cout << &myStruct.b << endl; // 003DFED2
cout << (int *)&myStruct.c << endl; // 003DFED4
在上面的例子中,char a offset為0 因成員對齊占據[D0]填充[D1]共兩個位元組,short b是最大長度成員無需對齊占據[D2-D3]兩個位元組,它的offset是2,而char c的offset是4占據[D4]無需成員對齊,但此時struct的大小是2+2+1 = 5位元組,不是2的整數倍,所以我們要填充空子節在最後直到struct大小達到2的整數倍,這就是整體對齊。
經過了數據成員對齊與整體對齊之後內存對齊就完成了,如果深入思考上述規則還會發現:即使是同樣數目與數量的數據成員,在擺放的順序不同時struct的大小也會不同,下面就是一個例子:
這樣擺放是12位元組:
卻變成了8位元組.png
由於這種特性,如果在網路編程或相關內存操作時如果不加以注意的話,就會造成隱秘而難以糾正的錯誤,請大家務必小心!