若是直接操做結構體成員是不會取到不指望的值
可是對於要求連續數據格式的時候須要考慮對齊的問題
例如通信中的數據幀格式等 ,如 ip數據包等
#pragma pack(1)
struct tagStruct
{
...
}
t;
#pragma pack()
的方式來強制連續存放
其中前面 pack(1) 是指對齊邊界爲 1c++
1。幾個結構體例子:編程
struct{
short a1;
short a2;
short a3;
}A;數組
struct{
long a1;
short a2;
}B;數據結構
sizeof( A)=6, sizeof( B)=8,爲何?
注:sizeof(short)=2,sizeof(long)=4架構
由於:「成員對齊有一個重要的條件,即每一個成員按本身的方式對齊。其對齊的規則是,每一個成員按其類型的對齊參數(一般是這個類型的大小)和指定對齊參數(這裏默認是8字節)中較小的一個對齊。而且結構的長度必須爲所用過的全部對齊參數的整數倍,不夠就補空字節。」(引用)app
結構體A中有3個short類型變量,各自以2字節對齊,結構體對齊參數按默認的8字節對齊,則a1,a2,a3都取2字節對齊,則sizeof(A)爲6,其也是2的整數倍;
B中a1爲4字節對齊,a2爲2字節對齊,結構體默認對齊參數爲8,則a1取4字節對齊,a2取2字節對齊,結構體大小6字節,6不爲4的整數倍,補空字節,增到8時,符合全部條件,則sizeof(B)爲8;函數
能夠設置成對齊的佈局
#pragma pack(1)
#pragma pack(push)
#pragma pack(1)編碼
struct{
short a1;
short a2;
short a3;
}A;spa
struct{
long a1;
short a2;
}B;
#pragma pack(pop)
結果爲sizeof( A)=6,sizeof( B)=6
************************
#pragma pack(8)
struct S1{
char a;
long b;
};
struct S2 {
char c;
struct S1 d;
long long e;
};
#pragma pack()
sizeof(S2)結果爲24.
成員對齊有一個重要的條件,即每一個成員分別對齊,即每一個成員按本身的方式對齊。
也就是說上面雖然指定了按8字節對齊,但並非全部的成員都是以8字節對齊。其對齊的規則是,每一個成員按其類型的對齊參數(一般是這個類型的大小)和指定對齊參數(這裏是8字節)中較小的一個對齊。而且結構的長度必須爲所用過的全部對齊參數的整數倍,不夠就補空字節。
S1中,成員a是1字節默認按1字節對齊,指定對齊參數爲8,這兩個值中取1,a按1字節對齊;成員b是4個字節,默認是按4字節對齊,這時就按4字節對齊,因此sizeof(S1)應該爲8;
S2 中,c和S1中的a同樣,按1字節對齊,而d 是個結構,它是8個字節,它按什麼對齊呢?對於結構來講,它的默認對齊方式就是它的全部成員使用的對齊參數 中最大的一個,S1的就是4.因此,成員d就是 按4字節對齊。成員e是8個字節,它是默認按8字節對齊,和指定的同樣,因此它對到8字節的邊界上,這時,已經使用了12個字節了,因此又添加了4個字節 的空,從第16個字節開始放置成員e。這時,長度爲24,已經能夠被8(成員e按8字節對齊)整除.這樣, 一共使用了24個字節。
a b
S1的內存佈局:1***, 1111,
c S1.a S1.b e
S2的內存佈局:1***, 1***, 1111, ****11111111
這裏有三點很重要:
1.每一個成員分別按本身的方式對齊,並能最小化長度
2.複雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是複雜類型時,能夠最小化長度
3.對齊後的長度必須是成員中最大的對齊參數的整數倍,這樣在處理數組時能夠保證每一項都邊界對齊
補充一下,對於數組,好比:
char a[3];這種,它的對齊方式和分別寫3個char是同樣的。也就是說它仍是按1個字節對齊。
若是寫: typedef char Array3[3];
Array3這種類型的對齊方式仍是按1個字節對齊,而不是按它的長度。
不論類型是什麼,對齊的邊界必定是1,2,4,8,16,32,64....中的一個。
/***********************/
字節對齊詳解
爲何要對齊?
現代計算機中內存空間都是按照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;
};
如今已知32位機器上各類數據類型的長度以下:
char:1(有符號無符號同)
short:2(有符號無符號同)
int:4(有符號無符號同)
long:4(有符號無符號同)
float:4 double:8
那麼上面兩個結構大小如何呢?
結果是:
sizeof(strcut A)值爲8
sizeof(struct B)的值倒是12
結構體A中包含了4字節長度的int一個,1字節長度的char一個和2字節長度的short型數據一個,B也同樣;按理說A,B大小應該都是7字節。
之因此出現上面的結果是由於編譯器要對數據成員在空間上進行對齊。上面是按照編譯器的默認設置進行對齊的結果,那麼咱們是否是能夠改變編譯器的這種默認對齊設置呢,固然能夠。例如:
#pragma pack (2) /*指定按2字節對齊*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定對齊,恢復缺省對齊*/
sizeof(struct C)值是8。
修改對齊值爲1:
#pragma pack (1) /*指定按1字節對齊*/
struct D
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定對齊,恢復缺省對齊*/
sizeof(struct D)值爲7。
後面咱們再講解#pragma pack()的做用。
三。編譯器是按照什麼樣的原則進行對齊的?
先讓咱們看四個重要的基本概念:
1.數據類型自身的對齊值:對於char型數據,其自身對齊值爲1,對於short型爲2,對於int,float,double類型,其自身對齊值爲4,單位字節。
2.結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。
3.指定對齊值:#pragma pack (value)時的指定對齊值value。
4.數據成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。
有 了這些值,咱們就能夠很方便的來討論具體數據結構的成員和其自身的對齊方式。有效對齊值N是最終用來決定數據存放地址方式的值,最重要。有效對齊N,就是 表示「對齊在N上」,也就是說該數據的「存放起始地址%N=0」。而數據結構中的數據變量都是按定義的前後順序來排放的。第一個數據變量的起始地址就是數 據結構的起始地址。結構體的成員變量要對齊排放,結構體自己也要根據自身的有效對齊值圓整(就是結構體成員變量佔用總長度須要是對結構體有效對齊值的整數 倍,結合下面例子理解)。這樣就不能理解上面的幾個例子的值了。
例子分析:
分析例子B:
struct B
{
char b;
int a;
short c;
};
假 設B從地址空間0x0000開始排放。該例子中沒有定義指定對齊值,在筆者環境下,該值默認爲4。第一個成員變量b的自身對齊值是1,比指定或者默認指定 對齊值4小,因此其有效對齊值爲1,因此其存放地址0x0000符合0x0000%1=0。第二個成員變量a,其自身對齊值爲4,因此有 效對齊值也爲4,因此只能存放在起始地址爲0x0004到0x0007這四個連續的字節空間中,符合0x0004%4=0,且緊靠第一個變量。第三個變量 c,自身對齊值爲2,因此有效對齊值也是2,能夠存放在0x0008到0x0009這兩個字節空間中,符合0x0008%2=0。因此從0x0000到 0x0009存放的都是B內容。再看數據結構B的自身對齊值爲其變量中最大對齊值(這裏是b)因此就是4,因此結構體的有效對齊值也是4。根據結構體圓整 的要求,0x0009到0x0000=10字節,(10+2)%4=0。因此0x0000A到0x000B也爲結構體B所佔用。故B從0x0000到 0x000B 共有12個字節,sizeof(struct B)=12;其實若是就這一個就來講它已將知足字節對齊了, 由於它的起始地址是0,所以確定是對齊的。之因此在後面補充2個字節,是由於編譯器爲了實現結構數組的存取效率,試想若是咱們定義了一個結構B的數組,那 麼第一個結構起始地址是0沒有問題,可是第二個結構呢?按照數組的定義,數組中全部元素都是緊挨着的,若是咱們不把結構的大小補充爲4的整數倍,那麼下一 個結構的起始地址將是0x0000A,這顯然不能知足結構的地址對齊了,所以咱們要把結構補充成有效對齊大小的整數倍。其實諸如:對於char型數據,其 自身對齊值爲1,對於short型爲2,對於int,float,double類型,其自身對齊值爲4,這些已有類型的自身對齊值也是基於數組考慮的,只 是由於這些類型的長度已知了,因此他們的自身對齊值也就已知了。
同理,分析上面例子C:
#pragma pack (2) /*指定按2字節對齊*/
struct C
{
char b;
int a;
short c;
};
#pragma pack () /*取消指定對齊,恢復缺省對齊*/
第 一個變量b的自身對齊值爲1,指定對齊值爲2,因此,其有效對齊值爲1,假設C從0x0000開始,那麼b存放在0x0000,符合 0x0000%1= 0;第二個變量,自身對齊值爲4,指定對齊值爲2,因此有效對齊值爲2,因此順序存放在0x000二、0x000三、0x000四、0x0005四個連續 字節中,符合0x0002%2=0。第三個變量c的自身對齊值爲2,因此有效對齊值爲2,順序存放在0x000六、0x0007中,符合 0x0006%2=0。因此從0x0000到0x00007共八字節存放的是C的變量。又C的自身對齊值爲4,因此C的有效對齊值爲2。又 8%2=0,C 只佔用0x0000到0x0007的八個字節。因此sizeof(struct C)=8。
四。如何修改編譯器的默認對齊值?
1.在VC IDE中,能夠這樣修改:[Project]|[Settings],c/c++選項卡Category的Code Generation選項的Struct Member Alignment中修改,默認是8字節。
2.在編碼時,能夠這樣動態修改:#pragma pack(value) 。注意:是pragma而不是progma.
五。針對字節對齊,咱們在編程中如何考慮?
如 果在編程的時候要考慮節約空間的話,那麼咱們只須要假定結構的首地址是0,而後各個變量按照上面的原則進行排列便可,基本的原則就是把結構中的變量按照類 型大小,從小到大聲明,儘可能減小中間的填補空間。還有一種就是爲了以空間換取時間的效率,咱們顯示的進行填補空間進行對齊,好比:有一種使用空間 換時間作法是顯式的插入reserved成員:
struct A{
char a;
char reserved[3];//使用空間換時間
int b;
}
reserved成員對咱們的程序沒有什麼意義,它只是起到填補空間以達到字節對齊的目的,固然即便不加這個成員一般編譯器也會給咱們自動填補對齊,咱們本身加上它只是起到顯式的提醒做用。
六。字節對齊可能帶來的隱患:
代碼中關於對齊的隱患,不少是隱式的。好比在強制類型轉換的時候。例如:
unsigned int i = 0x12345678;
unsigned char *p=NULL;
unsigned short *p1=NULL;
p=&i;
*p=0x00;
p1=(unsigned short *)(p+1);
*p1=0x0000;
最後兩句代碼,從奇數邊界去訪問unsignedshort型變量,顯然不符合對齊的規定。
在x86上,相似的操做只會影響效率,可是在MIPS或者sparc上,可能就是一個error,由於它們要求必須字節對齊。
七。如何查找與字節對齊方面的問題:
若是出現對齊或者賦值問題首先查看
1. 編譯器的big little端設置
2. 看這種體系自己是否支持非對齊訪問
3. 若是支持看設置了對齊與否,若是沒有則看訪問時須要加某些特殊的修飾來標誌其特殊訪問操做。
ARM下的對齊處理
from DUI0067D_ADS1_2_CompLib
3.13 type qulifiers
有部分摘自ARM編譯器文檔對齊部分
對齊的使用:
1.__align(num)
這個用於修改最高級別對象的字節邊界。在彙編中使用LDRD或者STRD時 就要用到此命令__align(8)進行修飾限制。來保證數據對象是相應 對齊。 這個修飾對象的命令最大是8個字節限制,可讓2字節的對象進行4字節 對齊,可是不能讓4字節的對象2字節對齊。 __align是存儲類修改,他只修飾最高級類型對象不能用於結構或者函數對象。
2.__packed
__packed是進行一字節對齊1.不能對packed的對象進行對齊2.全部對象的讀寫訪問都進行非對齊訪問
3.float及包含float的結構聯合及未用__packed的對象將不能字節對齊
4.__packed對局部整形變量無影響
5.強制由unpacked對象向packed對象轉化是未定義,整形指針能夠合法定義爲packed。 __packed int* p;//__packed int 則沒有意義
6.對齊或非對齊讀寫訪問帶來問題
__packed struct STRUCT_TEST
{
char a;
int b;
char c;
} ; //定義以下結構此時b的起始地址必定是不對齊的
//在棧中訪問b可能有問題,由於棧上數據確定是對齊訪問[from CL]
//將下面變量定義成全局靜態不在棧上
static char* p;
static struct STRUCT_TEST a;
void Main()
{
__packed int* q; //此時定義成__packed來修飾當前q指向爲非對齊的數據地址下面的訪問則能夠
p = (char*)&a;
q = (int*)(p+1);
*q = 0x87654321;
/*
獲得賦值的彙編指令很清楚
ldr r5,0x20001590 ; = #0x12345678
[0xe1a00005] mov r0,r5
[0xeb0000b0] bl __rt_uwrite4 //在此處調用一個寫4byte的操做函數
[0xe5c10000] strb r0,[r1,#0] //函數進行4次strb操做而後返回保證了數據正確的訪問
[0xe1a02420] mov r2,r0,lsr #8
[0xe5c12001] strb r2,[r1,#1]
[0xe1a02820] mov r2,r0,lsr #16
[0xe5c12002] strb r2,[r1,#2]
[0xe1a02c20] mov r2,r0,lsr #24
[0xe5c12003] strb r2,[r1,#3]
[0xe1a0f00e] mov pc,r14
*/
/*
若是q沒有加__packed修飾則彙編出來指令是這樣直接會致使奇地址處訪問失敗
[0xe59f2018] ldr r2,0x20001594 ; = #0x87654321
[0xe5812000] str r2,[r1,#0]
*/
//這樣能夠很清楚的看到非對齊訪問是如何產生錯誤的
//以及如何消除非對齊訪問帶來問題
//也能夠看到非對齊訪問和對齊訪問的指令差別致使效率問題
}
sizeof進行結構體大小的判斷
typedef struct
{
int a;
char b;
}A_t;
typedef struct
{
int a;
char b;
char c;
}B_t;
typedef struct
{
char a;
int b;
char c;
}C_t;
void main()
{
char*a=0;
cout<<sizeof(a)<<endl;//4
cout<<sizeof(*a)<<endl;//1--這個能理解
cout<<sizeof(A_t)<<endl;//8
cout<<sizeof(B_t)<<endl;//8
cout<<sizeof(C_t)<<endl;//12
}
爲何是這樣的結果啊?
2. 語法:
sizeof有三種語法形式,以下:
1) sizeof( object ); // sizeof( 對象 );
2) sizeof( type_name ); // sizeof( 類型 );
3) sizeof object; // sizeof 對象;
5. 指針變量的sizeof
既然是來存放地址的,那麼它固然等於計算機內部地址總線的寬度。因此在32位計算機中,一個指針變量的返回值一定是4(以字節爲單位),能夠預計,在未來的64位系統中指針變量的sizeof結果爲8。
char* pc = "abc";
int* pi;
string* ps;
char** ppc = &pc;
void (*pf)();// 函數指針
sizeof( pc ); // 結果爲4
sizeof( pi ); // 結果爲4
sizeof( ps ); // 結果爲4
sizeof( ppc ); // 結果爲4
sizeof( pf );// 結果爲4
指針變量的sizeof值與指針所指的對象沒有任何關係,正是因爲全部的指針變量所佔內存大小相等,因此MFC消息處理函數使用兩個參數WPARAM、LPARAM就能傳遞各類複雜的消息結構(使用指向結構體的指針)。
6. 數組的sizeof
數組的sizeof值等於數組所佔用的內存字節數
如:
char a1[] = "abc";
int a2[3];
sizeof( a1 ); // 結果爲4,字符串末尾還存在一個NULL終止符
sizeof( a2 ); // 結果爲3*4=12(依賴於int)
一些朋友剛開始時把sizeof看成了求數組元素的個數,如今,你應該知道這是不對的,那麼應該怎麼求數組元素的個數呢?Easy,一般有下面兩種寫法:
int c1 = sizeof( a1 ) / sizeof( char ); // 總長度/單個元素的長度
int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 總長度/第一個元素的長度
寫到這裏,提一問,下面的c3,c4值應該是多少呢?
void foo3(char a3[3])
{
int c3 = sizeof( a3 ); // c3 ==
}
void foo4(char a4[])
{
int c4 = sizeof( a4 ); // c4 ==
}
也 許當你試圖回答c4的值時已經意識到c3答錯了,是的,c3!=3。這裏函數參數a3已再也不是數組類型,而是蛻變成指針,至關於 char* a3,爲何?仔細想一想就不難明白,咱們調用函數foo1時,程序會在棧上分配一個大小爲3的數組嗎?不會!數組是「傳址」的,調用者只需將實參的地址傳 遞過去,因此a3天然爲指針類型(char*),c3的值也就爲4。
7. 結構體的sizeof
這是初學者問得最多的一個問題,因此這裏有必要多費點筆墨。讓咱們先看一個結構體:
struct S1
{
char c;
int i;
};
問sizeof(s1)等於多少?聰明的你開始思考了,char佔1個字節,int佔4個字節,那麼加起來就應該是5。是這樣嗎?你在你機器上試過了嗎?也許你是對的,但極可能你是錯的!VC6中按默認設置獲得的結果爲8。
Why?爲何受傷的老是我?請不要沮喪,咱們來好好琢磨一下sizeof的定義——sizeof的結果等於對象或者類型所佔的內存字節數,好吧,那就讓咱們來看看S1的內存分配狀況:
S1 s1 = { a , 0xFFFFFFFF };
定義上面的變量後,加上斷點,運行程序,觀察s1所在的內存,你發現了什麼?
以個人VC6.0爲例,s1的地址爲0x0012FF78,其數據內容以下:
0012FF78: 61 CC CC CC FF FF FF FF
發現了什麼?怎麼中間夾雜了3個字節的CC?看看MSDN上的說明:
When applied to a structure type or variable, sizeof returns the actual size,
which may include padding bytes inserted for alignment.
原 來如此,這就是傳說中的字節對齊啊!一個重要的話題出現了。爲何須要字節對齊?計算機組成原理教導咱們這樣有助於加快計算機的取數速度,不然就得多花指 令週期了。爲此,編譯器默認會對結構體進行處理(實際上其它地方的數據變量也是如此),讓寬度爲2的基本數據類型(short等)都位於能被2整除的地址 上,讓寬度爲 4的基本數據類型(int等)都位於能被4整除的地址上,以此類推。這樣,兩個數中間就可能須要加入填充字節,因此整個結構體的sizeof值就增加了。 讓咱們交換一下S1中char與int的位置:
struct S2
{
int i;
char c;
};
看看sizeof(S2)的結果爲多少,怎麼仍是8?再看看內存,原來成員c後面仍然有3個填充字節,這又是爲何啊?彆着急,下面總結規律。
字節對齊的細節和編譯器實現相關,但通常而言,知足三個準則:
1) 結構體變量的首地址可以被其最寬基本類型成員的大小所整除;
2) 結構體每一個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,若有須要編譯器會在成員之間加上填充字節(internal adding);
3) 結構體的總大小爲結構體最寬基本類型成員大小的整數倍,若有須要編譯器會在最末一個成員以後加上填充字節(trailing padding)。
對於上面的準則,有幾點須要說明:
1) 前 面不是說結構體成員的地址是其大小的整數倍,怎麼又說到偏移量了呢?由於有了第1點存在,因此咱們就能夠只考慮成員的偏移量,這樣思考起來簡單。想一想爲什 麼。結構體某個成員相對於結構體首地址的偏移量能夠經過宏offsetof()來得到,這個宏也在stddef.h中定義,以下:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
例如,想要得到S2中c的偏移量,方法爲
size_t pos = offsetof(S2, c);// pos等於4
2) 基本類型是指前面提到的像char、short、int、float、double這樣的內置數據類型,這裏所說的「數據寬度」就是指其sizeof的大 小。因爲結構體的成員能夠是複合類型,好比另一個結構體,因此在尋找最寬基本類型成員時,應當包括複合類型成員的子成員,而不是把複合成員當作是一個整 體。但在肯定複合類型成員的偏移位置時則是將複合類型做爲總體看待。這裏敘述起來有點拗口,思考起來也有點撓頭,仍是讓咱們看看例子吧(具體數值仍以 VC6爲例,之後再也不說明):
struct S3
{
char c1;
S1 s;
char c2
};
S1的最寬簡單成員的類型爲int,S3在考慮最寬簡單類型成員時是將S1「打散」看的,因此S3的最寬簡單類型爲int,這樣,經過S3定義的變量,其存儲空間首地址須要被4整除,整個sizeof(S3)的值也應該被4整除。
c1 的偏移量爲0,s的偏移量呢?這時s是一個總體,它做爲結構體變量也知足前面三個準則,因此其大小爲8,偏移量爲4,c1與s之間便須要3個填充字節,而 c2與s之間就不須要了,因此c2的偏移量爲12,算上c2的大小爲13,13是不能被4整除的,這樣末尾還得補上3個填充字節。最後獲得sizeof (S3)的值爲16。
經過上面的敘述,咱們能夠獲得一個公式:
結構體的大小等於最後一個成員的偏移量加上其大小再加上末尾的填充字節數目,即:
sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )
到 這裏,朋友們應該對結構體的sizeof有了一個全新的認識,但不要高興得太早,有一個影響sizeof的重要參量還未被說起,那即是編譯器的 pack指令。它是用來調整結構體對齊方式的,不一樣編譯器名稱和用法略有不一樣,VC6中經過#pragma pack實現,也能夠直接修改/Zp編譯開關。#pragma pack的基本用法爲:#pragma pack( n ),n爲字節對齊數,其取值爲一、二、四、八、16,默認是8,若是這個值比結構體成員的sizeof值小,那麼該成員的偏移量應該以此值爲準,便是說, 結構體成員的偏移量應該取兩者的最小值,公式以下:
offsetof( item ) = min( n, sizeof( item ) )
再看示例:
#pragma pack(push) // 將當前pack設置壓棧保存
#pragma pack(2)// 必須在結構體定義以前使用
struct S1
{
char c;
int i;
};
struct S3
{
char c1;
S1 s;
char c2
};
#pragma pack(pop) // 恢復先前的pack設置
計 算sizeof(S1)時,min(2, sizeof(i))的值爲2,因此i的偏移量爲2,加上sizeof(i)等於6,可以被2整除,因此整個S1的大小爲6。 一樣,對於sizeof(S3),s的偏移量爲2,c2的偏移量爲8,加上sizeof(c2)等於9,不能被2整除,添加一個填充字節,因此 sizeof(S3)等於10。如今,朋友們能夠輕鬆的出一口氣了,還有一點要注意,「空結構體」(不含數據成員)的大小不爲0,而是1。試想一個「不佔 空間」的變量如何被取地址、兩個不一樣的「空結構體」變量又如何得以區分呢?因而,「空結構體」變量也得被存儲,這樣編譯器也就只能爲其分配一個字節的空間 用於佔位了。
以下:
struct S5 { };
sizeof( S5 ); // 結果爲1
8. 含位域結構體的sizeof
前 面已經說過,位域成員不能單獨被取sizeof值,咱們這裏要討論的是含有位域的結構體的 sizeof,只是考慮到其特殊性而將其專門列了出來。C99規定int、unsigned int和bool能夠做爲位域類型,但編譯器幾乎都對此做了擴展,容許其它類型類型的存在。
使用位域的主要目的是壓縮存儲,其大體規則爲:
1) 若是相鄰位域字段的類型相同,且其位寬之和小於類型的sizeof大小,則後面的字段將緊鄰前一個字段存儲,直到不能容納爲止;
2) 若是相鄰位域字段的類型相同,但其位寬之和大於類型的sizeof大小,則後面的字段將重新的存儲單元開始,其偏移量爲其類型大小的整數倍;
3) 若是相鄰的位域字段的類型不一樣,則各編譯器的具體實現有差別,VC6採起不壓縮方式,Dev-C++採起壓縮方式;
4) 若是位域字段之間穿插着非位域字段,則不進行壓縮;
5) 整個結構體的總大小爲最寬基本類型成員大小的整數倍。
仍是讓咱們來看看例子。
示例1:
struct BF1
{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};
其內存佈局爲:
|_f1__|__f2__|_|____f3___|____|
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
0 3 7 8 1316
位域類型爲char,第1個字節僅能容納下f1和f2,因此f2被壓縮到第1個字節中,而f3只能從下一個字節開始。所以sizeof(BF1)的結果爲2。
示例2:
struct BF2
{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};
因爲相鄰位域類型不一樣,在VC6中其sizeof爲6,在Dev-C++中爲2。
示例3:
struct BF3
{
char f1 : 3;
char f2;
char f3 : 5;
};
非位域字段穿插在其中,不會產生壓縮,在VC6和Dev-C++中獲得的大小均爲3。
9. 聯合體的sizeof
結構體在內存組織上是順序式的,聯合體則是重疊式,各成員共享一段內存,因此整個聯合體的sizeof也就是每一個成員sizeof的最大值。結構體的成員也能夠是複合類型,這裏,複合類型成員是被做爲總體考慮的。
因此,下面例子中,U的sizeof值等於sizeof(s)。
union U { int i; char c; S1 s; };