字節(jié)對(duì)齊課件_第1頁(yè)
字節(jié)對(duì)齊課件_第2頁(yè)
字節(jié)對(duì)齊課件_第3頁(yè)
字節(jié)對(duì)齊課件_第4頁(yè)
字節(jié)對(duì)齊課件_第5頁(yè)
已閱讀5頁(yè),還剩29頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論