




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
字節(jié)對(duì)齊
AndrewHuang<bluedrum@163.com>
內(nèi)容提要
?字節(jié)對(duì)齊概念
?字節(jié)對(duì)齊測(cè)試
■offsetof
■缺省情況的字節(jié)對(duì)齊
■double型字節(jié)對(duì)齊
■改變字節(jié)對(duì)齊設(shè)置
?不同環(huán)境下的字節(jié)對(duì)齊
■GCC字節(jié)對(duì)齊
■ADS字節(jié)對(duì)齊
?字節(jié)對(duì)齊練習(xí)
字節(jié)對(duì)齊是一個(gè)很隱含的概念,平時(shí)可能你沒(méi)有留意,但是如果你在編寫(xiě)網(wǎng)絡(luò)通訊程序或者
用結(jié)構(gòu)去操作文件或硬件通訊結(jié)構(gòu),這個(gè)問(wèn)題就會(huì)浮出水面。我記得第?次導(dǎo)致我去看字節(jié)
對(duì)齊概念資料的原因就是ARP通訊,ARP包頭是一個(gè)31Byte包頭。當(dāng)你用一個(gè)認(rèn)為是31Byte
結(jié)構(gòu)去處理數(shù)據(jù)包時(shí),卻總是處理不對(duì)。這一篇文章詳細(xì)討論了字節(jié)對(duì)齊要領(lǐng)和各種情況.
字節(jié)對(duì)齊概念
?現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對(duì)任何類(lèi)型的變量的
訪問(wèn)可以從任何地址開(kāi)始,但為了CPU訪問(wèn)數(shù)據(jù)的快速,通常都要求數(shù)據(jù)存放的地址是有一
定規(guī)律的.
?比如在32位CPU上,一般要求變量地址都是基于4位,這樣可以保證CPU用一次的
讀寫(xiě)周期就可以讀取變量.不按4位對(duì)齊,如果變量剛好跨4位編碼,這樣需要CPU兩個(gè)讀寫(xiě)
周期.效率自然低下.因此,在現(xiàn)代的編譯器都會(huì)自動(dòng)把復(fù)合數(shù)據(jù)定義按4位對(duì)齊,以保證CPU
以最快速度讀取,這就是字節(jié)對(duì)齊(byteAlignment)產(chǎn)生的背景
?字節(jié)對(duì)齊是?種典型,以空間換時(shí)間的策略的,在現(xiàn)代計(jì)算機(jī)擁有較大的內(nèi)存的情況,
這個(gè)策略是相當(dāng)成功的.
為什么要字節(jié)對(duì)齊?
?加快程序訪問(wèn)速度
?很多CPU對(duì)訪問(wèn)地址有嚴(yán)格要求,這時(shí)編譯器必須要這個(gè)CPU的規(guī)范來(lái)實(shí)現(xiàn),X86
較為寬松,不對(duì)齊結(jié)構(gòu)可能只影響效率,如ARM,訪問(wèn)地址必須基于偶地址,MIPS和
Sparc也類(lèi)似,這樣不對(duì)齊的地址訪問(wèn)會(huì)造成錯(cuò)誤.
關(guān)于字節(jié)對(duì)齊的實(shí)現(xiàn)
在不同的CPU對(duì)地址對(duì)齊有不同要求,各個(gè)編譯器也會(huì)采用不同策略來(lái)實(shí)現(xiàn)字節(jié)對(duì)
齊,在隨后的例子,可以對(duì)比PCF的Windows,和Linux,以有ARM下的字節(jié)對(duì)齊策略.
字節(jié)對(duì)齊帶來(lái)的問(wèn)題
字節(jié)對(duì)齊相當(dāng)于編譯器自己在開(kāi)發(fā)者定義的結(jié)構(gòu)里偷偷加入一些填充字符,而且各種
編譯器填充的策略不一定相同.因此,在網(wǎng)絡(luò)傳輸,二進(jìn)制文件處理以及.底層總線傳輸和底層
數(shù)據(jù)等相關(guān)領(lǐng)域,忽略字節(jié)對(duì)齊會(huì)帶來(lái)嚴(yán)重問(wèn)題.這樣會(huì)產(chǎn)生錯(cuò)位使用程序處理數(shù)據(jù)完全錯(cuò)誤.
因此,網(wǎng)絡(luò)以及硬件相關(guān)開(kāi)發(fā)人員必須對(duì)字節(jié)對(duì)齊要有清晰的了解.
字節(jié)對(duì)齊測(cè)試
offsetof操作符
在分析字節(jié)對(duì)齊之前,首先了解一下offsetof宏.這個(gè)宏是標(biāo)準(zhǔn)C的定義,每個(gè)C庫(kù)均會(huì)在
stddefh中定義.作用是計(jì)算結(jié)構(gòu)或聯(lián)合每一個(gè)成員的偏移量.用。他etof我們可以很清晰看到
字節(jié)是如何對(duì)齊的.
它的用法如下:
typedefstruct{charcl;intil;charc2;}S3;
printfileIoffset=%d,i1offset=%d,c2oflfcet=%d/n'',
offsetof(S3,cl),offsetof(S3,il),ofTsetof(S3,c2));
offsetof在不同操作系統(tǒng)下定成不同形式.
/*Keil8051*/
#defineoffsetofi[s,m)(size_t)&(((s*)0)->m)
/*Microsoftx86*/
#ifdef_WIN64
#defineoffsetof(s,m)(size_t)((ptrdif^t)&(((s*)0)->m))
#else
#defineoffsetof(s,m)(size_t)&(((s*)0)->m)
#endif
/*Motorolacoldfire*/
#defineoflsetof(s,memb)((size_t)((char*)&((s*)0)->memb-(char*)0))
/*GNUGCC4.0.2*/
#defineoffsetof(TYPE,MEMBER)_builtin_offsetof(TYPE,MEMBER)
注意:。昧etof不能求位域成員的偏移量,o抵etof雖然引用了一個(gè)空指針來(lái)操作成員,但是
由于只是在取類(lèi)型,并且這個(gè)值在編譯期就被確定,所以編譯器在編譯會(huì)直接算出。昧etof的
值,而不會(huì)在運(yùn)行期引起內(nèi)存段錯(cuò)誤.
以下我們用。抵etof來(lái)分析結(jié)構(gòu)和字節(jié)對(duì)齊
缺省情況的字節(jié)對(duì)齊
缺省的情況我們是指32BitCPU,Windows使用VC++6Q用這個(gè)環(huán)境基本能說(shuō)明問(wèn)題,其
余的環(huán)境有不同的,再補(bǔ)充說(shuō)明.
對(duì)齊有如下情況:
1.基本類(lèi)型變量起始地址要按一定規(guī)則對(duì)齊.
?char類(lèi)型,其起始地址要1字節(jié)邊界上,即其地址能被1整除(即任意地址即可)
?short類(lèi)型,其起始地址要2字節(jié)邊界上,即其地址能被2整除
?int類(lèi)型,其起始地址要4字節(jié)邊界匕即其地址能被4整除
?long類(lèi)型,其起始地址要4字節(jié)邊界上,即其地址能被4整除
?float類(lèi)型,其起始地址要4字節(jié)邊界上,即其地址能被4整除
?double類(lèi)型,其起始地址要8字節(jié)邊界上,即其地址能被8整除
2.結(jié)構(gòu)實(shí)例起始址要在自己最大尺寸成員的對(duì)齊地址上
如最大尺寸的成員是short,則要基于2對(duì)齊
3.結(jié)構(gòu)內(nèi)成員的偏移量也要參照第1條,滿足相應(yīng)倍數(shù)
如成員是short,則偏移量也是2的倍數(shù).
這一條實(shí)際仍然是第1條規(guī)則的擴(kuò)展,因?yàn)榻Y(jié)構(gòu)起始地址按最大倍數(shù)來(lái),加上內(nèi)部相
應(yīng)倍數(shù),這樣成員絕對(duì)地址仍然滿足第1條規(guī)定
4.結(jié)構(gòu)總尺寸也要對(duì)齊.要為最大尺寸的成員的整數(shù)倍,
如果不是則要在結(jié)構(gòu)最后補(bǔ)齊成整數(shù)倍
關(guān)于第一條,我們做如下測(cè)試
charcl;
intil;
shortol;
doubledl;
#defineADDR_DIFF(a,b)((char*)a)-((char*)b)
printt^ncladdr=0x%x,i1addr=0x%x,oladdr=Ox%x,dladdr=0x%x/nM,
&cl,&il,&ol,&dl);
printffc1-il=%d,il-o1=%d,o1-dl=%d/nu,
ADDR_DIFF(&cl,&il),ADDR_DIFF(&il,&ol),ADDR_DIFF(&o1,&d1));
Win32下測(cè)試結(jié)果:
claddr=0x12fT7c,i1addr=Oxl2fF78,oladdr=0xl2fF74,dladdr=0x12fI6c
cl-il=4,il-ol=4,ol-dl=8
從測(cè)試結(jié)果可以看出,編譯器并沒(méi)有緊密的把各個(gè)數(shù)據(jù)結(jié)構(gòu)排列在一起,而是按其對(duì)齊地
址進(jìn)行分配
結(jié)構(gòu)的字節(jié)對(duì)齊
例1:
typedefstructs2{
inta;
shortb;
charc;
}s2;
printf(ns2size=%d,inta=%d,shortb=%d,charc=%d/n",
sizeof(s2),oflfsetof(s2,a),oilsetof(s2,b),offsetof(s2,c));
測(cè)試結(jié)果是s2size=8,inta=0,shortb=4,charc=6
從結(jié)果看.是總尺寸是8,各成員尺寸之和是7,從偏移量可以看在最后補(bǔ)齊一個(gè)字符,這是
按規(guī)則4,總尺寸是最大成員倍數(shù)
04
例加?A(
a
inia;
shortb;
charc;
};
編譯器自動(dòng)補(bǔ)齊1個(gè)bvte讓息尺
寸是最4■字節(jié)數(shù)倍數(shù)
bIuedrum@163.com
例2:
typedefstructs5{
inta;
charb;
shortc;
}s5;
printf("s5size=%d,inta=%d,charb=%d,shortc=%d/nH,
sizeof(s5),offsetof(s5,a),offsetof(s5,b),offsetofi[s5,c));
測(cè)試結(jié)果是s5size=8,inta=0,charb=4,shortc=6
這一次補(bǔ)齊的目的是為了short型的c基于2對(duì)齊,應(yīng)用第3條規(guī)則
例staotA{
inia;
charb;
shortc;
};編譯器自動(dòng)補(bǔ)齊1個(gè)byte.讓c的
起始地址基于2的倍數(shù)
bIuedrum@163.com
例3:
typedefstructslO{
charb;
shortc;
)slO;
printf(nslOsize=%d,charb=%d,inta=%d,shortc=%d/n",
sizeof(s10),oflfsetof(s10,b),offsetof(s10,a),offsetof(s10,c));
測(cè)試結(jié)果:slOsize=12,charb=0,inta=4,shortc=8
第?次補(bǔ)齊的目的是為了int型的a基于4對(duì)齊,應(yīng)用第3條規(guī)則
第二次補(bǔ)齊為了合符第4條規(guī)則.要為int的倍數(shù).
編譯器自動(dòng)補(bǔ)齊3個(gè)byte
讓a能基于4對(duì)齊
04
例staietB{
charb;
iflia;
shortc;
);
編譯器自動(dòng)補(bǔ)齊2個(gè)byte
讓總尺寸是4的倍數(shù)
bIuedrum@163.com
例5:
typedefstructs4{
chara;
shortb;
charc;
}s4;
printfi[ns4size=%d,inta=%d,shortb=%d,charc=%d/n”,
sizeof(s4),oflsetof(s4,a),offsetof(s4,b),oflfsetof(s4,c));
測(cè)試結(jié)果:s4size=6,inta=0,shortb=2,charc=4
這里最大尺寸的成員是shortb所以總尺寸是2的倍數(shù),而且short本身也需要2對(duì)齊,因此在
兩個(gè)不同地方補(bǔ)了一個(gè)byte
double型的字節(jié)對(duì)齊
先看測(cè)試樣例
typedefstructsi{
chara;
doubleb;
shortc;
}sl;
printff'slsize=%d,chara=%d,doubleb=%d,shortc=%d/nu,
sizeof(s1),offsetof(s1,a),offsetof(s1,b),oflfsetof(sl,c));
在Windows+VC6.0下測(cè)試結(jié)果:sisize=24,chara=0,doubleb=8,shortc=16
在Redhat9.0+gcc3.2.2下測(cè)試結(jié)果:sisize=16,chara=0,doubleb=4,shortc=12
可以看到在兩個(gè)編譯器上,對(duì)double的對(duì)齊處理不一樣.在Linux下,double采用是基于4對(duì)
齊.而Windows采用8對(duì)齊.
再看一個(gè)實(shí)例
typedefstructsi{
chara;
doubleb;
charc;
intd;
}sl;
printf(ns6size=%d,chara=%d,doubleb=%d,charc=%dintd=%d/n”,
sizeof(s6),offsetof(s6,a),offsetof(s6,b),offsetof(s6,c),oflsetof{s6,(i));
在Windows+VC6.0下測(cè)試結(jié)果:s6size=24,chara=0,doubleb=8,charc=16intd=20
Windows(基于8對(duì)齊)編譯器自動(dòng)補(bǔ)齊7個(gè)byte
讓a能基于8對(duì)齊
例沏^B{
charb;
doublea;
shortc;
);
編譯器自動(dòng)補(bǔ)齊6個(gè)byte
讓總尺寸是8的倍數(shù)’
bIuedrum6163.com
在Redhat9.0+gcc3.2.2下測(cè)試結(jié)果:s6size=20,chara=0,doubleb=4,charc=12intd=16
編譯器自動(dòng)補(bǔ)齊3Tbyte
?Linux(基于4對(duì)齊)讓a能基于4對(duì)齊
例{
charb;
doublea;
shortc;
};
編譯器自動(dòng)補(bǔ)齊2個(gè)byte
讓總尺寸是4的倍數(shù)
hIA3ucm
改變字節(jié)對(duì)齊設(shè)置
默認(rèn)的字節(jié)對(duì)齊都是按最大成員尺寸來(lái)進(jìn)行對(duì)齊,但是在開(kāi)發(fā)中可能需要調(diào)整對(duì)齊寬度.
最常的一種情況是,在在網(wǎng)絡(luò)和底層傳輸中取消字節(jié)對(duì)齊,完成按原始尺寸緊密的排列.
還有一種情況是擴(kuò)大或縮少字節(jié)對(duì)齊的排列.這種情況比較復(fù)雜.但應(yīng)用比較少.
取消字節(jié)對(duì)齊
在文件處理,網(wǎng)絡(luò)和底層傳輸中,數(shù)據(jù)都是緊密排列.不希望編譯器在結(jié)構(gòu)內(nèi)部自行增加
空間.這時(shí)需要開(kāi)發(fā)者通知編譯器,某一些結(jié)構(gòu)是不需要字節(jié)對(duì)齊的.
絕大部分編譯器是使用預(yù)編譯指令pragma取消對(duì)齊
?#pragmapack(n)設(shè)置對(duì)齊寬度為n,它可以是1,2,4,8等等,其中1就表示不進(jìn)行字
節(jié)對(duì)齊.
■#pragmapack(n)是成片生效的,即在這個(gè)指令后面所有結(jié)構(gòu)都會(huì)按新的對(duì)齊
值進(jìn)行對(duì)齊
?#pragmapack()將上一次#pragmapack(n)的設(shè)置取消.恢復(fù)為默認(rèn)值.
?兩者是成對(duì)使用,在這兩者之間所有結(jié)構(gòu)均受到影響
注意是pragma,不是progma
例子:
#pragmapack(l)
typedefstructs7{
inta;
shortb;
charc;
}s7;
#pragmapack()
printfl;"s7size=%d,inta=%d,shortb=%d,charc=%d/n",
sizeof(s7),offsetof(s7,a),oflfsetof(s7,b),ofTsetof(s7,c));
測(cè)試結(jié)果s7size=7,inta=0,shortb=4,charc=6
可以看到,取消字節(jié)對(duì)齊,sizeof()就成員尺寸之和.
改變字節(jié)對(duì)齊
這種情況比較復(fù)雜,而且也不常用.也是通過(guò)#pragmapack(n)來(lái)完成生效,但是要注意,
字節(jié)對(duì)齊值采用n和默認(rèn)對(duì)齊值中較小的一個(gè).換句話說(shuō),擴(kuò)大對(duì)齊值是不生效的.
#pragmapack還有其它功能
?#pragmapack(push)//將當(dāng)前pack設(shè)置壓棧保存
?#pragmapack(pop)〃恢復(fù)先前的pack設(shè)置
這兩個(gè)功能用于多種對(duì)齊值混用的場(chǎng)合,(當(dāng)然,這種情況也是非常少見(jiàn))
縮小例子:
#pragmapack(2)/*指定按2字節(jié)對(duì)齊,缺省是4*/
typedefstructs8
{
chara;
intb;
shortc;
)s8;
#pragmapack()
printfi[ns8size=%d,chara=%d,intb=%d,shortc=%d/n”,
sizeof(s8),offsetof(s8,a),offsetof(s8,b),offsetof(s8,c));
測(cè)試結(jié)果:s8size=8,chara=0,intb=2,shortc=6
缺省的4字節(jié)對(duì)齊話,sizoef應(yīng)該是12,現(xiàn)在改為2對(duì)齊的話,只能是8,即在chara補(bǔ)了一
個(gè)字節(jié).
擴(kuò)大的例子:
#pragmapack(8)/*指定按2字節(jié)對(duì)齊,缺省是4*/
typedefstructs9
|
chara;
intb;
shortc;
}s9;
#pragmapack()
printf(Ms9size=%d,chara=%d,intb=%d,shortc=%d/n”,
sizeof(s9),offsetof(s9,a),oflsetofi[s9,b),offsetof(s9,c));
測(cè)試結(jié)果:s9size=12,chara=0,intb=4,shortc=8
這個(gè)結(jié)果跟4對(duì)齊是一樣的,換句話說(shuō),8對(duì)齊沒(méi)有生效
不同環(huán)境下的字節(jié)對(duì)齊使用
GCC的字節(jié)對(duì)齊控制
GCC也支持#pragma字節(jié)控制
?#pragmapack(n),gcc將按照n個(gè)字節(jié)對(duì)齊
?#pragmapack(),取消自定義字節(jié)對(duì)齊方式
#pragma只保證的成員相關(guān)偏移量是字節(jié)對(duì)齊的.不保證絕對(duì)地址對(duì)齊.
GCC也支持某個(gè)?個(gè)數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)絕對(duì)地址的自然對(duì)齊
—attribute((aligned(n)))讓所作用的結(jié)構(gòu)成員對(duì)齊在n字節(jié)自然邊界上。如果結(jié)構(gòu)中有
成員的長(zhǎng)度大于n,則按照最大成員的長(zhǎng)度來(lái)對(duì)齊。
_attribute_((packed)),取消結(jié)構(gòu)在編譯過(guò)程中的優(yōu)化對(duì)齊,按照實(shí)際占用字節(jié)數(shù)進(jìn)行
對(duì)齊。
structSTRUCT_TEST
{
chara;
intb;
charc;
}_attribute_((packed));〃注意位置,在}與;之間
?_attribute是GCC屬性,跟#pragma不同,_attribute_是gcc的方言,只有GCC能
識(shí)別,不要在VC++之類(lèi)編譯器使用這種定義.
attribute每次只對(duì)一個(gè)結(jié)構(gòu)生效.
ADS的字節(jié)對(duì)齊控制
ARM對(duì)訪問(wèn)地址有特殊要求,如果不對(duì)齊,會(huì)造成程序錯(cuò)誤,而不是象X86或PowerPC
那樣折成兩個(gè)指令訪問(wèn).因此用#pragmapack(l)只是讓結(jié)構(gòu)本身成員內(nèi)部按1對(duì)齊,并不
能保證結(jié)構(gòu)的絕對(duì)地址是對(duì)齊.
ADS采用特殊指令來(lái)實(shí)現(xiàn)要想保證地址對(duì)齊.ADS采用
ALIGN._align(num),._packed,來(lái)控制字節(jié)對(duì)齊
?ALIGN用于匯編的字節(jié)對(duì)齊控制
?_align(num)類(lèi)似于#pragmapack(num),用于整片代碼字節(jié)對(duì)齊的的控制.
?_packed取消某個(gè)結(jié)構(gòu)或成員的內(nèi)部字節(jié)對(duì)齊,并實(shí)現(xiàn)絕對(duì)地址對(duì)齊,類(lèi)似于gcc的
_attribute_((packed));
_packedstructSTRUCTTEST
(
chara;
intb;
charc;
);
字節(jié)對(duì)齊練習(xí)
請(qǐng)指在windows32下出下列值的sizeof和各個(gè)成員的偏移量
1.structsi{
shorta;
shortb;
shortc;
};
2.structs2{
chara[21];
shortb;
);
3.structs2{
floata;
charb;
shortc;
intd;
);
5.#pragmapack(1)
typedefstructs8
{
chara;
intb;
shortc;
doubled;
}s8;
#pragmapack()
c語(yǔ)言字節(jié)對(duì)齊(以32位系統(tǒng)為例)
(2011-02-1616:36:31)
轉(zhuǎn)載
畫(huà)標(biāo)簽:分類(lèi):C語(yǔ)言程序設(shè)計(jì)
科技
結(jié)構(gòu)體
字節(jié)
讀周期
編譯器
字節(jié)對(duì)齊
數(shù)據(jù)對(duì)齊
教育
1.什么是對(duì)齊?
現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照字節(jié)(byte)劃分的,從理
論上講似乎對(duì)任何類(lèi)型的變量的訪問(wèn)可以從任何地址開(kāi)始,但實(shí)
際情況是在訪問(wèn)特定變量的時(shí)候經(jīng)常在特定的內(nèi)存地址訪問(wèn),這
就需要各類(lèi)型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序地
一個(gè)接一個(gè)地排放,這就是對(duì)齊。
2.計(jì)算機(jī)為什么要對(duì)齊?
各個(gè)硬件平臺(tái)對(duì)存儲(chǔ)空間的處理上有很大的不同。一些平臺(tái)
對(duì)某些特定類(lèi)型的數(shù)據(jù)只能從某些特定地址開(kāi)始存取,其他平臺(tái)
可能沒(méi)有這種情況。但是最常見(jiàn)的是,如果不按照適合其平臺(tái)的
要求對(duì)數(shù)據(jù)存放進(jìn)行對(duì)齊,會(huì)在存取效率上帶來(lái)?yè)p失。比如有些
平臺(tái)每次讀都是從偶地址開(kāi)始,一個(gè)int型(假設(shè)為32位)如
果存放在偶地址開(kāi)始的地方,那么一個(gè)讀周期就可以讀出,而如
果存放在奇地址開(kāi)始的地方,就可能會(huì)需要2個(gè)讀周期,并對(duì)兩
次讀出的結(jié)果的高低字節(jié)進(jìn)行拼湊才能得到該int數(shù)據(jù),顯然在
讀取效率上下降很多。這也是空間和時(shí)間的博弈。在網(wǎng)絡(luò)程序中,
掌握這個(gè)概念可是很重要的:如果在不同平臺(tái)之間(比如在
Windows和Linux之間)傳遞2進(jìn)制流(比如結(jié)構(gòu)體),那么在這
兩個(gè)平臺(tái)間必須要定義相同的對(duì)齊方式,不然莫名其妙地出了一
些錯(cuò),可是很難排查的。
3.對(duì)齊的實(shí)現(xiàn):
通常,我們寫(xiě)程序的時(shí)候,不需要考慮對(duì)齊問(wèn)題,編譯器會(huì)
替我們選擇適合目標(biāo)平臺(tái)的對(duì)齊策略。當(dāng)然,我們也可以通知給
編譯器傳遞預(yù)編譯指令而改變對(duì)指定數(shù)據(jù)的對(duì)齊方法,比如寫(xiě)入
預(yù)編譯指令#pragmapack(2),即告訴編譯器按兩字節(jié)對(duì)齊。
但是,正因?yàn)槲覀円话悴恍枰P(guān)心這個(gè)問(wèn)題,所以,如果編
輯器對(duì)數(shù)據(jù)存放做了對(duì)齊,而我們不了解的話,常常會(huì)對(duì)一些問(wèn)
題感到迷惑。最常見(jiàn)的就是struct數(shù)據(jù)結(jié)構(gòu)的sizeof結(jié)果,比
如以下程序:
#include<stdio.h>
intmain(void)
structA
chara;
shortb;
intc;
);
printf(〃結(jié)構(gòu)體類(lèi)型A在內(nèi)存中所占內(nèi)存為:%d字節(jié)。
\n,z,sizeof(structA));
return0;
)
輸出結(jié)果為:8字節(jié)。
如果我們將結(jié)構(gòu)體中的變量聲明位置稍加改動(dòng)(并不改變變
量本身),請(qǐng)?jiān)倏匆韵鲁绦颍?/p>
#include<stdio.h>
intmain(void)
{
structA
{
shortb;
intc;
chara;
);
printf(〃結(jié)構(gòu)體類(lèi)型A在內(nèi)存中所占內(nèi)存為:%d字節(jié)。
\n,z,sizeof(structA));
return0;
)
輸出結(jié)果為:12字節(jié)。
問(wèn)題出來(lái)了,他們都是同一個(gè)結(jié)構(gòu)體,為什么占用的內(nèi)存大
小不同呢?為此,我們需要對(duì)對(duì)齊算法有所了解。
4.對(duì)齊算法:
由于各個(gè)平臺(tái)和編譯器的不同,現(xiàn)以32位,vc++6.0系統(tǒng)
為例,來(lái)討論編譯器對(duì)struct數(shù)據(jù)結(jié)構(gòu)中的各成員如何進(jìn)行對(duì)
齊的。
首先,我們給出四個(gè)概念:
1)數(shù)據(jù)類(lèi)型自身的對(duì)齊值:就是基本數(shù)據(jù)類(lèi)型的自身對(duì)齊值,
比如char類(lèi)型的自身對(duì)齊值為1字節(jié),int類(lèi)型的自身對(duì)齊值
為4字節(jié)。
2)指定對(duì)齊值:預(yù)編譯命令#pragmapack(value)指定的對(duì)
齊值value。
3)結(jié)構(gòu)體或者類(lèi)的自身對(duì)齊值:其成員中自身對(duì)齊值最大的那
個(gè)值,比如以上的structA的對(duì)齊值為4。
4)數(shù)據(jù)成員、結(jié)構(gòu)體和類(lèi)的有效對(duì)齊值:自身對(duì)齊值和指定對(duì)
齊值中較小的那個(gè)值。
設(shè)結(jié)構(gòu)體如下定義:
structA
chara;
shortb;
intc;
);
a是char型數(shù)據(jù),占用1字節(jié)內(nèi)存;short型數(shù)據(jù),占用2
字節(jié)內(nèi)存;int型數(shù)據(jù),占用4字節(jié)內(nèi)存。因此,結(jié)構(gòu)體A的自
身對(duì)齊值為4。于是,a和b要組成4個(gè)字節(jié),以便與c的4個(gè)
字節(jié)對(duì)齊。而a只有1個(gè)字節(jié),a與b之間便空了一個(gè)字節(jié)。我
們知道,結(jié)構(gòu)體類(lèi)型數(shù)據(jù)是按順序存儲(chǔ)結(jié)構(gòu)一個(gè)接一個(gè)向后排列
的,于是其存儲(chǔ)方式為:
其中空白方格無(wú)數(shù)據(jù),是浪費(fèi)的內(nèi)存空間,共占用8字節(jié)內(nèi)
存。
實(shí)際上,為了更加明顯地表示“對(duì)齊”,我們可以將以上結(jié)
構(gòu)想象為以下的行排列:
ab
a和b與c對(duì)齊
對(duì)于另一個(gè)結(jié)構(gòu)體定義:
structA
shortb;
intc;
chara;
);
其內(nèi)存存儲(chǔ)方式為:
同樣把它想象成行排列:
可見(jiàn),浪費(fèi)的空間更多。
其實(shí),除了結(jié)構(gòu)體之外,整個(gè)程序在給每個(gè)變量進(jìn)行內(nèi)存分
配時(shí)都會(huì)遵循對(duì)齊機(jī)制,也都會(huì)產(chǎn)生內(nèi)存空間的浪費(fèi)。但我們要
知道,這種浪費(fèi)是值得的,因?yàn)樗鼡Q來(lái)的是效率的提高。
以上分析都是建立在程序默認(rèn)的對(duì)齊值基礎(chǔ)之上的,我們可
以通過(guò)添加預(yù)定義命☆#pragmapack(value)來(lái)對(duì)對(duì)齊值進(jìn)行自
定義,比如#pragmapack(l),對(duì)齊值變?yōu)?,此時(shí)內(nèi)存緊湊,
不會(huì)出現(xiàn)內(nèi)存浪費(fèi),但效率降低了。效率之所以降低,是因?yàn)椋?/p>
如果存在更大字節(jié)數(shù)的變量時(shí)(比1大),比如int類(lèi)型,需要
進(jìn)行多次讀周期才能將一個(gè)int數(shù)據(jù)拼湊起來(lái)。
一、關(guān)于字節(jié)對(duì)齊,這可能是我找到的解釋最為準(zhǔn)確的一篇文章了,尤其對(duì)于
#pragmapack的解釋.之前看了好幾篇文章,都解釋為是設(shè)置默認(rèn)對(duì)齊字節(jié)數(shù).
唯有該篇指出是設(shè)置字節(jié)對(duì)齊時(shí)所允許的最大值.經(jīng)linux下驗(yàn)證,符合事實(shí).
似乎網(wǎng)上的文章以訛傳訛的情況越來(lái)越多了.以至于關(guān)于集線器在osi體系中所
處的層次居然有3中說(shuō)法.
朋友帖了如下一段代碼:
ttpragmapack(4)
classTestB
(
public:
intaa;
chara;
shortb;
charc;
);
intnSize=sizeof(TestB);
這里nSize結(jié)果為12,在預(yù)料之中。
現(xiàn)在去掉第一個(gè)成員變量為如下代碼:
Spragmapack(4)
classTestC
(
public:
chara;
shortb;
charc;
);
intnSize=sizeof(TestC);
按照正常的填充方式nSize的結(jié)果應(yīng)該是8,為什么結(jié)果顯示nSize為
6呢?
事實(shí)上,很多人對(duì)#pragmapack的理解是錯(cuò)誤的。
Spragmapack規(guī)定的對(duì)齊長(zhǎng)度,實(shí)際使用的規(guī)則是:
結(jié)構(gòu),聯(lián)合,或者類(lèi)的數(shù)據(jù)成員,第一個(gè)放在偏移為0的地方,以后每個(gè)數(shù)
據(jù)成員的對(duì)齊,按照#pragmapack指定的數(shù)值和這個(gè)數(shù)據(jù)成員自身長(zhǎng)度中,比
較小的那個(gè)進(jìn)行。
也就是說(shuō),當(dāng)#pragmapack的值等于或超過(guò)所有數(shù)據(jù)成員長(zhǎng)度的時(shí)候,這
個(gè)值的大小將不產(chǎn)生任何效果。
而結(jié)構(gòu)整體的對(duì)齊,則按照結(jié)構(gòu)體中最大的數(shù)據(jù)成員和#pragmapack指
定值之間,較小的那個(gè)進(jìn)行。
具體解釋
Spragmapack(4)
classTestB
(
public:
intaa;〃第一一個(gè)成員,放在[0,3]偏移的位置,
chara;〃第二個(gè)成員,自身長(zhǎng)為1,#pragmapack(4),取小值,
也就是1,所以這個(gè)成員按一字節(jié)對(duì)齊,放在偏移[4]的位置。
shortb;〃第三個(gè)成員,自身長(zhǎng)2,#pragmapack(4),取2,按
2字節(jié)對(duì)齊,所以放在偏移[6,7]的位置。
charc;〃第四個(gè),自身長(zhǎng)為1,放在[8]的位置。
);
這個(gè)類(lèi)實(shí)際占據(jù)的內(nèi)存空間是9字節(jié)
類(lèi)之間的對(duì)齊,是按照類(lèi)內(nèi)部最大的成員的長(zhǎng)度,和#pragmapack規(guī)定的
值之中較小的一個(gè)對(duì)齊的。
所以這個(gè)例子中,類(lèi)之間對(duì)齊的長(zhǎng)度是min(sizeof(int),4),也就是4。
9按照4字節(jié)圓整的結(jié)果是12,所以sizeof(TestB)是12。
如果
Spragmapack(2)
classTestB
(
public:
intaa;〃第一個(gè)成員,放在[0,3]偏移的位置,
chara;〃第二個(gè)成員,自身長(zhǎng)為1,^pragmapack(4),取小值,
也就是1,所以這個(gè)成員按一字節(jié)對(duì)齊,放在偏移[4]的位置。
shortb;〃第三個(gè)成員,自身長(zhǎng)2,弁pragmapack(4),取2,按
2字節(jié)對(duì)齊,所以放在偏移[6,7]的位置。
charc;〃第四個(gè),自身長(zhǎng)為1,放在[8]的位置。
);
〃可以看出,上面的位置完全沒(méi)有變化,只是類(lèi)之間改為按2字節(jié)對(duì)齊,9
按2圓整的結(jié)果是10。
〃所以sizeof(TestB)是10。
最后看原貼:
現(xiàn)在去掉第一個(gè)成員變量為如下代碼:
Spragmapack(4)
classTestC
public:
chara;〃第一個(gè)成員,放在[0]偏移的位置,
shortb;〃第二個(gè)成員,自身長(zhǎng)2,Spragmapack(4),取2,按2
字節(jié)對(duì)齊,所以放在偏移⑵3]的位置。
charc;〃第三個(gè),自身長(zhǎng)為1,放在[4]的位置。
);
//整個(gè)類(lèi)的大小是5字節(jié),按照min(sizeof(short),4)字節(jié)對(duì)齊,也就是2
字節(jié)對(duì)齊,結(jié)果是6
//所以sizeof(TestC)是6。
感謝Michael提出疑問(wèn),在此補(bǔ)充:
當(dāng)數(shù)據(jù)定義中出現(xiàn)—declspec(align。)時(shí),指定類(lèi)型的對(duì)齊長(zhǎng)度還要用
自身長(zhǎng)度和這里指定的數(shù)值比較,然后取其中較大的。最終類(lèi)/結(jié)構(gòu)的對(duì)齊長(zhǎng)度
也需要和這個(gè)數(shù)值比較,然后取其中較大的。
可以這樣理解,―declspec(align())和#pragmapack是一■對(duì)兄弟,
前者規(guī)定了對(duì)齊的最小值,后者規(guī)定了對(duì)齊的最大值,兩者同時(shí)出現(xiàn)時(shí),前者擁
有更高的優(yōu)先級(jí)。
_declspec(align())的一個(gè)特點(diǎn)是,它僅僅規(guī)定了數(shù)據(jù)對(duì)齊的位置,而
沒(méi)有規(guī)定數(shù)據(jù)實(shí)際占用的內(nèi)存長(zhǎng)度,當(dāng)指定的數(shù)據(jù)被放置在確定的位置之后,其
后的數(shù)據(jù)填充仍然是按照#pragmapack規(guī)定的方式填充的,這時(shí)候類(lèi)/結(jié)構(gòu)的實(shí)
際大小和內(nèi)存格局的規(guī)則是這樣的:
在—declspec(align())之前,數(shù)據(jù)按照#pragmapack規(guī)定的方式填充,
如前所述。當(dāng)遇到_declspec(alignO)的時(shí)候,首先尋找距離當(dāng)前偏移向后
最近的對(duì)齊點(diǎn)(滿足對(duì)齊長(zhǎng)度為max(數(shù)據(jù)自身長(zhǎng)度,指定值)),然后把被指定
的數(shù)據(jù)類(lèi)型從這個(gè)點(diǎn)開(kāi)始填充,其后的數(shù)據(jù)類(lèi)型從它的后面開(kāi)始,仍然按照
^pragmapack填充,直到遇到下一個(gè)_declspec(align())。
當(dāng)所有數(shù)據(jù)填充完畢,把結(jié)構(gòu)的整體對(duì)齊數(shù)值和—declspec(alignO)規(guī)
定的值做比較,取其中較大的作為整個(gè)結(jié)構(gòu)的對(duì)齊長(zhǎng)度。
特別的,當(dāng)—declspec(alignO)指定的數(shù)值比對(duì)應(yīng)類(lèi)型長(zhǎng)度小的時(shí)候,
這個(gè)指定不起作用。
二、1,比如:
struct{
shortal;
shorta2;
shorta3;
}A;
struct{
longal;
shorta2;
}B;
sizeof(A)=6,sizeof(B)=8,為什么?
注:sizeof(short)=2,sizeof(long)=4
因?yàn)椋骸俺蓡T對(duì)齊有一個(gè)重要的條件,即每個(gè)成員按自己的方式對(duì)齊.其對(duì)齊的規(guī)則是,每個(gè)成員
按其類(lèi)型的對(duì)齊參數(shù)(通常是這個(gè)類(lèi)型的大小)和指定對(duì)齊參數(shù)(這里默認(rèn)是8字節(jié))中較小的一
個(gè)對(duì)齊.并且結(jié)構(gòu)的長(zhǎng)度必須為所用過(guò)的所有對(duì)齊參數(shù)的整數(shù)倍,不夠就補(bǔ)空字節(jié).”(引用)
結(jié)構(gòu)體A中有3個(gè)short類(lèi)型變量,各自以2字節(jié)對(duì)齊,結(jié)構(gòu)體對(duì)齊參數(shù)按默認(rèn)的8字節(jié)對(duì)齊,
則al,a2,a3都取2字節(jié)對(duì)齊,則sizeof(A)為6,其也是2的整數(shù)倍;
B中al為4字節(jié)對(duì)齊,a2為2字節(jié)對(duì)齊,結(jié)構(gòu)體默認(rèn)對(duì)齊參數(shù)為8,則al取4字節(jié)對(duì)齊,a2
取2字節(jié)對(duì)齊,結(jié)構(gòu)體大小6字節(jié),6不為4的整數(shù)倍,補(bǔ)空字節(jié),增到8時(shí),符合所有條件,
則sizeof(B)為8;
可以設(shè)置成對(duì)齊的
#pragmapack(l)
#pragmapack(push)
#pragmapack(l)
struct{
shortal;
shorta2;
shorta3;
}A;
struct{
longal;
shorta2;
}B;
ttpragmapack(pop)結(jié)果為sizeof(A)=6,sizeof(B)=6
2,又如:
#pragmapack(8)
structSI{
chara;
longb;
);
structS2{
charc;
structSId;
longlonge;
);
^pragmapack()
sizeof(S2)結(jié)果為24.
成員對(duì)齊有一個(gè)重要的條件,即每個(gè)成員分別對(duì)齊.即每個(gè)成員按自己的方式對(duì)齊.
也就是說(shuō)上面雖然指定了按8字節(jié)對(duì)齊,但并不是所有的成員都是以8字節(jié)對(duì)齊.其對(duì)齊的規(guī)則
是,每個(gè)成員按其類(lèi)型的對(duì)齊參數(shù)(通常是這個(gè)類(lèi)型的大?。┖椭付▽?duì)齊參數(shù)(這里是8字節(jié))中較
小的一個(gè)對(duì)齊.并且結(jié)構(gòu)的長(zhǎng)度必須為所用過(guò)的所有對(duì)齊參數(shù)的整數(shù)倍,不夠就補(bǔ)空字節(jié).
S1中,成員a是1字節(jié)默認(rèn)按1字節(jié)對(duì)齊,指定對(duì)齊參數(shù)為8,這兩個(gè)值中取1,a按1字節(jié)對(duì)齊;
成員b是4個(gè)字節(jié),默認(rèn)是按4字節(jié)對(duì)齊,這時(shí)就按4字節(jié)對(duì)齊,所以sizeof($1)應(yīng)該為8;
S2中,c和S1中的a一樣,按1字節(jié)對(duì)齊,而d是個(gè)結(jié)構(gòu),它是8個(gè)字節(jié),它按什么對(duì)齊呢?對(duì)于
結(jié)構(gòu)來(lái)說(shuō),它的默認(rèn)對(duì)齊方式就是它的所有成員使用的對(duì)齊參數(shù)中最大的一個(gè),S1的就是4.所
以,成員d就是按4字節(jié)對(duì)齊.成員e是8個(gè)字節(jié),它是默認(rèn)按8字節(jié)對(duì)齊,和指定的一樣,所以它
對(duì)到8字節(jié)的邊界上,這時(shí),已經(jīng)使用了12個(gè)字節(jié)了,所以又添加了4個(gè)字節(jié)的空,從第16個(gè)字
節(jié)開(kāi)始放置成員e.這時(shí),長(zhǎng)度為24,已經(jīng)可以被8(成員e按8字節(jié)對(duì)齊)整除.這樣,一共使用了
24個(gè)字節(jié).
ab
S1的內(nèi)存布局:1***,1111,
cSI.aSI.be
S2的內(nèi)存布局:1***,1***,1111,****11111111
這里有三點(diǎn)很重要:
1.每個(gè)成員分別按自己的方式對(duì)齊,并能最小化長(zhǎng)度
2.復(fù)雜類(lèi)型(如結(jié)構(gòu))的默認(rèn)對(duì)齊方式是它最長(zhǎng)的成員的對(duì)齊方式,這樣在成員是復(fù)雜類(lèi)型時(shí),可
以最小化長(zhǎng)度
3.對(duì)齊后的長(zhǎng)度必須是成員中最大的對(duì)齊參數(shù)的整數(shù)倍,這樣在處理數(shù)組時(shí)可以保證每一項(xiàng)都邊
界對(duì)齊
補(bǔ)充一下,對(duì)于數(shù)組,比如:
chara[3];這種,它的對(duì)齊方式和分別寫(xiě)3個(gè)char是一樣的.也就是說(shuō)它還是按1個(gè)字節(jié)對(duì)齊.
如果寫(xiě):typedefcharArray3[3];
Array3這種類(lèi)型的對(duì)齊方式還是按1個(gè)字節(jié)對(duì)齊,而不是按它的長(zhǎng)度.
不論類(lèi)型是什么,對(duì)齊的邊界一定是1,2,4,8,16,32,64....中的一個(gè).
字節(jié)對(duì)齊詳解
為什么要對(duì)齊?
現(xiàn)代計(jì)算機(jī)中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對(duì)任何類(lèi)型
的變量的訪問(wèn)可以從任何地址開(kāi)始,但實(shí)際情況是在訪問(wèn)特定類(lèi)型變量的時(shí)候經(jīng)
常在特定的內(nèi)存地址訪問(wèn),這就需要各種類(lèi)型數(shù)據(jù)按照一定的規(guī)則在空間上排
列,而不是順序的一個(gè)接一個(gè)的排放,這就是對(duì)齊。
對(duì)齊的作用和原因:各個(gè)硬件平臺(tái)對(duì)存儲(chǔ)空間的處理上有很大的不同。一些
平臺(tái)對(duì)某些特定類(lèi)型的數(shù)據(jù)只能從某些特定地址開(kāi)始存取。比如有些架構(gòu)的CPU
在訪問(wèn)一個(gè)沒(méi)有進(jìn)行對(duì)齊的變量的時(shí)候會(huì)發(fā)生錯(cuò)誤,那么在這種架構(gòu)下編程必
須保證字節(jié)對(duì)齊.其他平臺(tái)可能沒(méi)有這種情況,但是最常見(jiàn)的是如果不按照適合
其平臺(tái)要求對(duì)數(shù)據(jù)存放進(jìn)行對(duì)齊,會(huì)在存取效率上帶來(lái)?yè)p失。比如有些平臺(tái)每
次讀都是從偶地址開(kāi)始,如果一個(gè)int型(假設(shè)為32位系統(tǒng))如果存放在偶地
址開(kāi)始的地方,那么一個(gè)讀周期就可以讀出這32bit,而如果存放在奇地址開(kāi)
始的地方,就需要2個(gè)讀周期,并對(duì)兩次讀出的結(jié)果的高低字節(jié)進(jìn)行拼湊才能得
到該32bit數(shù)據(jù)。顯然在讀取效率上下降很多。
二.字節(jié)對(duì)齊對(duì)程序的影響:
先讓我們看兒個(gè)例子吧(32bit,x86環(huán)境,gcc編譯器):
設(shè)結(jié)構(gòu)體如下定義:
structA
(
inta;
charb;
shortc;
);
structB
{
charb;
inta;
shortc;
);
現(xiàn)在已知32位機(jī)器上各種數(shù)據(jù)類(lèi)型的長(zhǎng)度如下:
char:1(有符號(hào)無(wú)符號(hào)同)
short:2(有符號(hào)無(wú)符號(hào)同)
int:4(有符號(hào)無(wú)符號(hào)同)
long:4(有符號(hào)無(wú)符號(hào)同)
float:4double:8
那么上面兩個(gè)結(jié)構(gòu)大小如何呢?
結(jié)果是:
sizeof(strcutA)值為8
sizeof(structB)的值卻是12
結(jié)構(gòu)體A中包含了4字節(jié)長(zhǎng)度的int一個(gè),1字節(jié)長(zhǎng)度的char一個(gè)和2字節(jié)長(zhǎng)
度的short型數(shù)據(jù)一個(gè),B也一樣;按理說(shuō)A,B大小應(yīng)該都是7字節(jié)。
之所以出現(xiàn)上面的結(jié)果是因?yàn)榫幾g器要對(duì)數(shù)據(jù)成員在空間上進(jìn)行對(duì)齊。上面是按
照編譯器的默認(rèn)設(shè)置進(jìn)行對(duì)齊的結(jié)果,那么我們是不是可以改變編譯器的這種默
認(rèn)對(duì)齊設(shè)置呢,當(dāng)然可以.例如:
#pragmapack(2)/*指定按2字節(jié)對(duì)齊*/
structC
(
charb;
inta;
shortc;
);
#pragmapack()/*取消指定對(duì)齊,恢復(fù)缺省對(duì)齊*/
sizeof(structC)值是8。
修改對(duì)齊值為1:
#pragmapack(1)/*指定按1字節(jié)對(duì)齊*/
structD
{
charb:
inta;
shortc;
);
#pragmapack()/*取消指定對(duì)齊,恢復(fù)缺省對(duì)齊*/
sizeof(structD)值為7。
后面我們?cè)僦v解#pragmapack。的作用.
三.編譯器是按照什么樣的原則進(jìn)行對(duì)齊的?
先讓我們看四個(gè)重要的基本概念:
1.數(shù)據(jù)類(lèi)型自身的對(duì)齊值:
對(duì)于char型數(shù)據(jù),其自身對(duì)齊值為1,對(duì)于short型為2,對(duì)于
int,float,double類(lèi)型,其自身對(duì)齊值為4,單位字節(jié)。
2.結(jié)構(gòu)體或者類(lèi)的自身對(duì)齊值:其成員中自身對(duì)齊值最大的那個(gè)值。
3.指定對(duì)齊值:Spragmapack(value)時(shí)的指定對(duì)齊值value。
4.數(shù)據(jù)成員、結(jié)構(gòu)體和類(lèi)的有效對(duì)齊值:自身對(duì)齊值和指定對(duì)齊值中小的那個(gè)值。
有了這些值,我們就可以很方便的來(lái)討論具體數(shù)據(jù)結(jié)構(gòu)的成員和其自身的對(duì)齊
方式。有效對(duì)齊值N是最終用來(lái)決定數(shù)據(jù)存放地址方式的值,最重要。有效對(duì)齊
N,就是表示“對(duì)齊在N上”,也就是說(shuō)該數(shù)據(jù)的“存放起始地址潁=0〃.而數(shù)據(jù)
結(jié)構(gòu)中的數(shù)據(jù)變量都是按定義的先后順序來(lái)排放的。第一個(gè)數(shù)據(jù)變量的起始地址
就是數(shù)據(jù)結(jié)構(gòu)的起始地址。結(jié)構(gòu)體的成員變量要對(duì)齊排放,結(jié)構(gòu)體本身也要根
據(jù)自身的有效對(duì)齊值圓整(就是結(jié)構(gòu)體成員變量占用總長(zhǎng)度需要是對(duì)結(jié)構(gòu)體有效
對(duì)齊值的整數(shù)倍,結(jié)合下面例子理解)。這樣就不能理解上面的兒個(gè)例子的值了。
例子分析:
分析例子B;
structB
{
charb;
inta;
shortc;
);
假設(shè)B從地址空間0x0000開(kāi)始排放。該例子中沒(méi)有定義指定對(duì)齊值,在筆者環(huán)
境下,該值默認(rèn)為4。第一個(gè)成員變量b的自身對(duì)齊值是1,比指定或者默認(rèn)指
定對(duì)齊值4小,所以其有效對(duì)齊值為1,所以其存放地址0x0000符合
0x0000知=0.第二個(gè)成員變量a,其自身對(duì)齊值為4,所以有效對(duì)齊值也為4,所
以只能存放在起始地址為0x0004到0x0007這四個(gè)連續(xù)的字節(jié)空間中,復(fù)核
0x0004%4=0,且緊靠第一個(gè)變量。第三個(gè)變量c,自身對(duì)齊值為2,所以有效對(duì)齊
值也是2,可以存放在0x0008到0x0009這兩個(gè)字節(jié)空間中,符合0x0008%2=0。
所以從0x0000到0x0009存放的都是B內(nèi)容。再看數(shù)據(jù)結(jié)構(gòu)B的自身對(duì)齊值為
其變量中最大對(duì)齊值(這里是b)所以就是4,所以結(jié)構(gòu)體的有效對(duì)齊值也是4。
根據(jù)結(jié)構(gòu)體圓整的要求,0x0009到0x0000=10字節(jié),(10+2)%4=0。所以
OxOOOOA到OxOOOB也為結(jié)構(gòu)體B所占用。故B從0x0000到OxOOOB共有12個(gè)字
節(jié),sizeof(structB)=12;其實(shí)如果就這?個(gè)就來(lái)說(shuō)它已將滿足字節(jié)對(duì)齊了,因
為它的起始地址是0,因此肯定是對(duì)齊的,之所以在后面補(bǔ)充2個(gè)字節(jié),是因?yàn)榫?/p>
譯器為了實(shí)現(xiàn)結(jié)構(gòu)數(shù)組的存取效率,試想如果我們定義了?個(gè)結(jié)構(gòu)B的數(shù)組,那
么第一個(gè)結(jié)構(gòu)起始地址是0沒(méi)有問(wèn)題,但是第二個(gè)結(jié)構(gòu)呢?按照數(shù)組的定義,數(shù)組
中所有元素都是緊挨著的,如果我們不把結(jié)構(gòu)的大小補(bǔ)充為4的整數(shù)倍,那么下
一個(gè)結(jié)構(gòu)的起始地址將是OxOOOOA,這顯然不能滿足結(jié)構(gòu)的地址對(duì)齊了,因此我
們要把結(jié)構(gòu)補(bǔ)充成有效對(duì)齊大小的整數(shù)倍.其實(shí)諸如:對(duì)于char型數(shù)據(jù),其自身
對(duì)齊值為1,對(duì)于short型為2,對(duì)于int,float,double類(lèi)型,其自身對(duì)齊值為
4,這些已有類(lèi)型的自身對(duì)齊值也是基于數(shù)組考慮的,只是因?yàn)檫@些類(lèi)型的長(zhǎng)度
已知了,所以他們的自身對(duì)齊值也就已知了.
同理,分析上面例子C:
#pragmapack(2)/*指定按2字節(jié)對(duì)齊*/
structC
{
charb;
inta;
shortc;
);
#pragmapack()/*取消指定對(duì)齊,恢復(fù)缺省對(duì)齊*/
第一個(gè)變量b的自身對(duì)齊值為1,指定對(duì)齊值為2,所以,其有效對(duì)齊值為1,
假設(shè)C從0x0000開(kāi)始,那么b存放在0x0000,符合0x0000%l=0;第二個(gè)變量,
自身對(duì)齊值為4,指定對(duì)齊值為2,所以有效對(duì)齊值為2,所以順序存放在0x0002、
0x0003、0x0004、0x0005四個(gè)連續(xù)字節(jié)中,符合0x0002%2=0。第三個(gè)變量c
的自身對(duì)齊值為2,所以有效對(duì)齊值為2,順序存放
在0x0006、0x0007中,符合0x0006%2=0o所以從0x0000到0x00007共八字節(jié)
存放的是C的變量。又C的自身對(duì)齊值為4,所以C的有效對(duì)齊值為2。又8%2=0,C
只占用0x0
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 物業(yè)管理服務(wù)協(xié)議細(xì)則
- 公文寫(xiě)作的重要性與2025年試題及答案
- 通信行業(yè)智能化通信設(shè)備維護(hù)與升級(jí)方案
- 車(chē)位租賃共享協(xié)議
- 行政管理學(xué)考試思維導(dǎo)圖及試題及答案
- 自考行政管理知識(shí)總結(jié)試題及答案
- 行政管理學(xué)知識(shí)更新試題及答案
- 現(xiàn)代管理學(xué)思維模式的試題及答案
- 2025企業(yè)長(zhǎng)期借款合同模板
- 2025年挖掘機(jī)租賃合同
- 醫(yī)院科室6S管理制度
- RULES OF ORIGIN 原產(chǎn)地規(guī)則
- 國(guó)內(nèi)旅游出團(tuán)通知書(shū)(新版)
- LETTEROFINTENTION意向書(shū)范本
- 國(guó)內(nèi)各航空公司差異化服務(wù)
- 《山東省自然科學(xué)基金資助項(xiàng)目年度進(jìn)展報(bào)告》
- 發(fā)展與教育心理學(xué)個(gè)別差異
- 2022年重慶市建筑安全員A證考試近年真題匯總(含答案解析)
- 太倉(cāng)德資企業(yè)
- 電網(wǎng)有限公司電網(wǎng)建設(shè)項(xiàng)目檔案管理辦法
- 簡(jiǎn)易離職申請(qǐng)
評(píng)論
0/150
提交評(píng)論