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

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

字節(jié)對齊

AndrewHuang<bluedrum@163.com>

內容提要

?字節(jié)對齊概念

?字節(jié)對齊測試

■offsetof

■缺省情況的字節(jié)對齊

■double型字節(jié)對齊

■改變字節(jié)對齊設置

?不同環(huán)境下的字節(jié)對齊

■GCC字節(jié)對齊

■ADS字節(jié)對齊

?字節(jié)對齊練習

字節(jié)對齊是一個很隱含的概念,平時可能你沒有留意,但是如果你在編寫網絡通訊程序或者

用結構去操作文件或硬件通訊結構,這個問題就會浮出水面。我記得第?次導致我去看字節(jié)

對齊概念資料的原因就是ARP通訊,ARP包頭是一個31Byte包頭。當你用一個認為是31Byte

結構去處理數(shù)據(jù)包時,卻總是處理不對。這一篇文章詳細討論了字節(jié)對齊要領和各種情況.

字節(jié)對齊概念

?現(xiàn)代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的

訪問可以從任何地址開始,但為了CPU訪問數(shù)據(jù)的快速,通常都要求數(shù)據(jù)存放的地址是有一

定規(guī)律的.

?比如在32位CPU上,一般要求變量地址都是基于4位,這樣可以保證CPU用一次的

讀寫周期就可以讀取變量.不按4位對齊,如果變量剛好跨4位編碼,這樣需要CPU兩個讀寫

周期.效率自然低下.因此,在現(xiàn)代的編譯器都會自動把復合數(shù)據(jù)定義按4位對齊,以保證CPU

以最快速度讀取,這就是字節(jié)對齊(byteAlignment)產生的背景

?字節(jié)對齊是?種典型,以空間換時間的策略的,在現(xiàn)代計算機擁有較大的內存的情況,

這個策略是相當成功的.

為什么要字節(jié)對齊?

?加快程序訪問速度

?很多CPU對訪問地址有嚴格要求,這時編譯器必須要這個CPU的規(guī)范來實現(xiàn),X86

較為寬松,不對齊結構可能只影響效率,如ARM,訪問地址必須基于偶地址,MIPS和

Sparc也類似,這樣不對齊的地址訪問會造成錯誤.

關于字節(jié)對齊的實現(xiàn)

在不同的CPU對地址對齊有不同要求,各個編譯器也會采用不同策略來實現(xiàn)字節(jié)對

齊,在隨后的例子,可以對比PCF的Windows,和Linux,以有ARM下的字節(jié)對齊策略.

字節(jié)對齊帶來的問題

字節(jié)對齊相當于編譯器自己在開發(fā)者定義的結構里偷偷加入一些填充字符,而且各種

編譯器填充的策略不一定相同.因此,在網絡傳輸,二進制文件處理以及.底層總線傳輸和底層

數(shù)據(jù)等相關領域,忽略字節(jié)對齊會帶來嚴重問題.這樣會產生錯位使用程序處理數(shù)據(jù)完全錯誤.

因此,網絡以及硬件相關開發(fā)人員必須對字節(jié)對齊要有清晰的了解.

字節(jié)對齊測試

offsetof操作符

在分析字節(jié)對齊之前,首先了解一下offsetof宏.這個宏是標準C的定義,每個C庫均會在

stddefh中定義.作用是計算結構或聯(lián)合每一個成員的偏移量.用。他etof我們可以很清晰看到

字節(jié)是如何對齊的.

它的用法如下:

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雖然引用了一個空指針來操作成員,但是

由于只是在取類型,并且這個值在編譯期就被確定,所以編譯器在編譯會直接算出。昧etof的

值,而不會在運行期引起內存段錯誤.

以下我們用。抵etof來分析結構和字節(jié)對齊

缺省情況的字節(jié)對齊

缺省的情況我們是指32BitCPU,Windows使用VC++6Q用這個環(huán)境基本能說明問題,其

余的環(huán)境有不同的,再補充說明.

對齊有如下情況:

1.基本類型變量起始地址要按一定規(guī)則對齊.

?char類型,其起始地址要1字節(jié)邊界上,即其地址能被1整除(即任意地址即可)

?short類型,其起始地址要2字節(jié)邊界上,即其地址能被2整除

?int類型,其起始地址要4字節(jié)邊界匕即其地址能被4整除

?long類型,其起始地址要4字節(jié)邊界上,即其地址能被4整除

?float類型,其起始地址要4字節(jié)邊界上,即其地址能被4整除

?double類型,其起始地址要8字節(jié)邊界上,即其地址能被8整除

2.結構實例起始址要在自己最大尺寸成員的對齊地址上

如最大尺寸的成員是short,則要基于2對齊

3.結構內成員的偏移量也要參照第1條,滿足相應倍數(shù)

如成員是short,則偏移量也是2的倍數(shù).

這一條實際仍然是第1條規(guī)則的擴展,因為結構起始地址按最大倍數(shù)來,加上內部相

應倍數(shù),這樣成員絕對地址仍然滿足第1條規(guī)定

4.結構總尺寸也要對齊.要為最大尺寸的成員的整數(shù)倍,

如果不是則要在結構最后補齊成整數(shù)倍

關于第一條,我們做如下測試

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下測試結果:

claddr=0x12fT7c,i1addr=Oxl2fF78,oladdr=0xl2fF74,dladdr=0x12fI6c

cl-il=4,il-ol=4,ol-dl=8

從測試結果可以看出,編譯器并沒有緊密的把各個數(shù)據(jù)結構排列在一起,而是按其對齊地

址進行分配

結構的字節(jié)對齊

例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));

測試結果是s2size=8,inta=0,shortb=4,charc=6

從結果看.是總尺寸是8,各成員尺寸之和是7,從偏移量可以看在最后補齊一個字符,這是

按規(guī)則4,總尺寸是最大成員倍數(shù)

04

例加?A(

a

inia;

shortb;

charc;

};

編譯器自動補齊1個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));

測試結果是s5size=8,inta=0,charb=4,shortc=6

這一次補齊的目的是為了short型的c基于2對齊,應用第3條規(guī)則

例staotA{

inia;

charb;

shortc;

};編譯器自動補齊1個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));

測試結果:slOsize=12,charb=0,inta=4,shortc=8

第?次補齊的目的是為了int型的a基于4對齊,應用第3條規(guī)則

第二次補齊為了合符第4條規(guī)則.要為int的倍數(shù).

編譯器自動補齊3個byte

讓a能基于4對齊

04

例staietB{

charb;

iflia;

shortc;

);

編譯器自動補齊2個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));

測試結果:s4size=6,inta=0,shortb=2,charc=4

這里最大尺寸的成員是shortb所以總尺寸是2的倍數(shù),而且short本身也需要2對齊,因此在

兩個不同地方補了一個byte

double型的字節(jié)對齊

先看測試樣例

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下測試結果:sisize=24,chara=0,doubleb=8,shortc=16

在Redhat9.0+gcc3.2.2下測試結果:sisize=16,chara=0,doubleb=4,shortc=12

可以看到在兩個編譯器上,對double的對齊處理不一樣.在Linux下,double采用是基于4對

齊.而Windows采用8對齊.

再看一個實例

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下測試結果:s6size=24,chara=0,doubleb=8,charc=16intd=20

Windows(基于8對齊)編譯器自動補齊7個byte

讓a能基于8對齊

例沏^B{

charb;

doublea;

shortc;

);

編譯器自動補齊6個byte

讓總尺寸是8的倍數(shù)’

bIuedrum6163.com

在Redhat9.0+gcc3.2.2下測試結果:s6size=20,chara=0,doubleb=4,charc=12intd=16

編譯器自動補齊3Tbyte

?Linux(基于4對齊)讓a能基于4對齊

例{

charb;

doublea;

shortc;

};

編譯器自動補齊2個byte

讓總尺寸是4的倍數(shù)

hIA3ucm

改變字節(jié)對齊設置

默認的字節(jié)對齊都是按最大成員尺寸來進行對齊,但是在開發(fā)中可能需要調整對齊寬度.

最常的一種情況是,在在網絡和底層傳輸中取消字節(jié)對齊,完成按原始尺寸緊密的排列.

還有一種情況是擴大或縮少字節(jié)對齊的排列.這種情況比較復雜.但應用比較少.

取消字節(jié)對齊

在文件處理,網絡和底層傳輸中,數(shù)據(jù)都是緊密排列.不希望編譯器在結構內部自行增加

空間.這時需要開發(fā)者通知編譯器,某一些結構是不需要字節(jié)對齊的.

絕大部分編譯器是使用預編譯指令pragma取消對齊

?#pragmapack(n)設置對齊寬度為n,它可以是1,2,4,8等等,其中1就表示不進行字

節(jié)對齊.

■#pragmapack(n)是成片生效的,即在這個指令后面所有結構都會按新的對齊

值進行對齊

?#pragmapack()將上一次#pragmapack(n)的設置取消.恢復為默認值.

?兩者是成對使用,在這兩者之間所有結構均受到影響

注意是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));

測試結果s7size=7,inta=0,shortb=4,charc=6

可以看到,取消字節(jié)對齊,sizeof()就成員尺寸之和.

改變字節(jié)對齊

這種情況比較復雜,而且也不常用.也是通過#pragmapack(n)來完成生效,但是要注意,

字節(jié)對齊值采用n和默認對齊值中較小的一個.換句話說,擴大對齊值是不生效的.

#pragmapack還有其它功能

?#pragmapack(push)//將當前pack設置壓棧保存

?#pragmapack(pop)〃恢復先前的pack設置

這兩個功能用于多種對齊值混用的場合,(當然,這種情況也是非常少見)

縮小例子:

#pragmapack(2)/*指定按2字節(jié)對齊,缺省是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));

測試結果:s8size=8,chara=0,intb=2,shortc=6

缺省的4字節(jié)對齊話,sizoef應該是12,現(xiàn)在改為2對齊的話,只能是8,即在chara補了一

個字節(jié).

擴大的例子:

#pragmapack(8)/*指定按2字節(jié)對齊,缺省是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));

測試結果:s9size=12,chara=0,intb=4,shortc=8

這個結果跟4對齊是一樣的,換句話說,8對齊沒有生效

不同環(huán)境下的字節(jié)對齊使用

GCC的字節(jié)對齊控制

GCC也支持#pragma字節(jié)控制

?#pragmapack(n),gcc將按照n個字節(jié)對齊

?#pragmapack(),取消自定義字節(jié)對齊方式

#pragma只保證的成員相關偏移量是字節(jié)對齊的.不保證絕對地址對齊.

GCC也支持某個?個數(shù)據(jù)結構實現(xiàn)絕對地址的自然對齊

—attribute((aligned(n)))讓所作用的結構成員對齊在n字節(jié)自然邊界上。如果結構中有

成員的長度大于n,則按照最大成員的長度來對齊。

_attribute_((packed)),取消結構在編譯過程中的優(yōu)化對齊,按照實際占用字節(jié)數(shù)進行

對齊。

structSTRUCT_TEST

{

chara;

intb;

charc;

}_attribute_((packed));〃注意位置,在}與;之間

?_attribute是GCC屬性,跟#pragma不同,_attribute_是gcc的方言,只有GCC能

識別,不要在VC++之類編譯器使用這種定義.

attribute每次只對一個結構生效.

ADS的字節(jié)對齊控制

ARM對訪問地址有特殊要求,如果不對齊,會造成程序錯誤,而不是象X86或PowerPC

那樣折成兩個指令訪問.因此用#pragmapack(l)只是讓結構本身成員內部按1對齊,并不

能保證結構的絕對地址是對齊.

ADS采用特殊指令來實現(xiàn)要想保證地址對齊.ADS采用

ALIGN._align(num),._packed,來控制字節(jié)對齊

?ALIGN用于匯編的字節(jié)對齊控制

?_align(num)類似于#pragmapack(num),用于整片代碼字節(jié)對齊的的控制.

?_packed取消某個結構或成員的內部字節(jié)對齊,并實現(xiàn)絕對地址對齊,類似于gcc的

_attribute_((packed));

_packedstructSTRUCTTEST

(

chara;

intb;

charc;

);

字節(jié)對齊練習

請指在windows32下出下列值的sizeof和各個成員的偏移量

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語言字節(jié)對齊(以32位系統(tǒng)為例)

(2011-02-1616:36:31)

轉載

畫標簽:分類:C語言程序設計

科技

結構體

字節(jié)

讀周期

編譯器

字節(jié)對齊

數(shù)據(jù)對齊

教育

1.什么是對齊?

現(xiàn)代計算機中內存空間都是按照字節(jié)(byte)劃分的,從理

論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實

際情況是在訪問特定變量的時候經常在特定的內存地址訪問,這

就需要各類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序地

一個接一個地排放,這就是對齊。

2.計算機為什么要對齊?

各個硬件平臺對存儲空間的處理上有很大的不同。一些平臺

對某些特定類型的數(shù)據(jù)只能從某些特定地址開始存取,其他平臺

可能沒有這種情況。但是最常見的是,如果不按照適合其平臺的

要求對數(shù)據(jù)存放進行對齊,會在存取效率上帶來損失。比如有些

平臺每次讀都是從偶地址開始,一個int型(假設為32位)如

果存放在偶地址開始的地方,那么一個讀周期就可以讀出,而如

果存放在奇地址開始的地方,就可能會需要2個讀周期,并對兩

次讀出的結果的高低字節(jié)進行拼湊才能得到該int數(shù)據(jù),顯然在

讀取效率上下降很多。這也是空間和時間的博弈。在網絡程序中,

掌握這個概念可是很重要的:如果在不同平臺之間(比如在

Windows和Linux之間)傳遞2進制流(比如結構體),那么在這

兩個平臺間必須要定義相同的對齊方式,不然莫名其妙地出了一

些錯,可是很難排查的。

3.對齊的實現(xiàn):

通常,我們寫程序的時候,不需要考慮對齊問題,編譯器會

替我們選擇適合目標平臺的對齊策略。當然,我們也可以通知給

編譯器傳遞預編譯指令而改變對指定數(shù)據(jù)的對齊方法,比如寫入

預編譯指令#pragmapack(2),即告訴編譯器按兩字節(jié)對齊。

但是,正因為我們一般不需要關心這個問題,所以,如果編

輯器對數(shù)據(jù)存放做了對齊,而我們不了解的話,常常會對一些問

題感到迷惑。最常見的就是struct數(shù)據(jù)結構的sizeof結果,比

如以下程序:

#include<stdio.h>

intmain(void)

structA

chara;

shortb;

intc;

);

printf(〃結構體類型A在內存中所占內存為:%d字節(jié)。

\n,z,sizeof(structA));

return0;

)

輸出結果為:8字節(jié)。

如果我們將結構體中的變量聲明位置稍加改動(并不改變變

量本身),請再看以下程序:

#include<stdio.h>

intmain(void)

{

structA

{

shortb;

intc;

chara;

);

printf(〃結構體類型A在內存中所占內存為:%d字節(jié)。

\n,z,sizeof(structA));

return0;

)

輸出結果為:12字節(jié)。

問題出來了,他們都是同一個結構體,為什么占用的內存大

小不同呢?為此,我們需要對對齊算法有所了解。

4.對齊算法:

由于各個平臺和編譯器的不同,現(xiàn)以32位,vc++6.0系統(tǒng)

為例,來討論編譯器對struct數(shù)據(jù)結構中的各成員如何進行對

齊的。

首先,我們給出四個概念:

1)數(shù)據(jù)類型自身的對齊值:就是基本數(shù)據(jù)類型的自身對齊值,

比如char類型的自身對齊值為1字節(jié),int類型的自身對齊值

為4字節(jié)。

2)指定對齊值:預編譯命令#pragmapack(value)指定的對

齊值value。

3)結構體或者類的自身對齊值:其成員中自身對齊值最大的那

個值,比如以上的structA的對齊值為4。

4)數(shù)據(jù)成員、結構體和類的有效對齊值:自身對齊值和指定對

齊值中較小的那個值。

設結構體如下定義:

structA

chara;

shortb;

intc;

);

a是char型數(shù)據(jù),占用1字節(jié)內存;short型數(shù)據(jù),占用2

字節(jié)內存;int型數(shù)據(jù),占用4字節(jié)內存。因此,結構體A的自

身對齊值為4。于是,a和b要組成4個字節(jié),以便與c的4個

字節(jié)對齊。而a只有1個字節(jié),a與b之間便空了一個字節(jié)。我

們知道,結構體類型數(shù)據(jù)是按順序存儲結構一個接一個向后排列

的,于是其存儲方式為:

其中空白方格無數(shù)據(jù),是浪費的內存空間,共占用8字節(jié)內

存。

實際上,為了更加明顯地表示“對齊”,我們可以將以上結

構想象為以下的行排列:

ab

a和b與c對齊

對于另一個結構體定義:

structA

shortb;

intc;

chara;

);

其內存存儲方式為:

同樣把它想象成行排列:

可見,浪費的空間更多。

其實,除了結構體之外,整個程序在給每個變量進行內存分

配時都會遵循對齊機制,也都會產生內存空間的浪費。但我們要

知道,這種浪費是值得的,因為它換來的是效率的提高。

以上分析都是建立在程序默認的對齊值基礎之上的,我們可

以通過添加預定義命☆#pragmapack(value)來對對齊值進行自

定義,比如#pragmapack(l),對齊值變?yōu)?,此時內存緊湊,

不會出現(xiàn)內存浪費,但效率降低了。效率之所以降低,是因為:

如果存在更大字節(jié)數(shù)的變量時(比1大),比如int類型,需要

進行多次讀周期才能將一個int數(shù)據(jù)拼湊起來。

一、關于字節(jié)對齊,這可能是我找到的解釋最為準確的一篇文章了,尤其對于

#pragmapack的解釋.之前看了好幾篇文章,都解釋為是設置默認對齊字節(jié)數(shù).

唯有該篇指出是設置字節(jié)對齊時所允許的最大值.經linux下驗證,符合事實.

似乎網上的文章以訛傳訛的情況越來越多了.以至于關于集線器在osi體系中所

處的層次居然有3中說法.

朋友帖了如下一段代碼:

ttpragmapack(4)

classTestB

(

public:

intaa;

chara;

shortb;

charc;

);

intnSize=sizeof(TestB);

這里nSize結果為12,在預料之中。

現(xiàn)在去掉第一個成員變量為如下代碼:

Spragmapack(4)

classTestC

(

public:

chara;

shortb;

charc;

);

intnSize=sizeof(TestC);

按照正常的填充方式nSize的結果應該是8,為什么結果顯示nSize為

6呢?

事實上,很多人對#pragmapack的理解是錯誤的。

Spragmapack規(guī)定的對齊長度,實際使用的規(guī)則是:

結構,聯(lián)合,或者類的數(shù)據(jù)成員,第一個放在偏移為0的地方,以后每個數(shù)

據(jù)成員的對齊,按照#pragmapack指定的數(shù)值和這個數(shù)據(jù)成員自身長度中,比

較小的那個進行。

也就是說,當#pragmapack的值等于或超過所有數(shù)據(jù)成員長度的時候,這

個值的大小將不產生任何效果。

而結構整體的對齊,則按照結構體中最大的數(shù)據(jù)成員和#pragmapack指

定值之間,較小的那個進行。

具體解釋

Spragmapack(4)

classTestB

(

public:

intaa;〃第一一個成員,放在[0,3]偏移的位置,

chara;〃第二個成員,自身長為1,#pragmapack(4),取小值,

也就是1,所以這個成員按一字節(jié)對齊,放在偏移[4]的位置。

shortb;〃第三個成員,自身長2,#pragmapack(4),取2,按

2字節(jié)對齊,所以放在偏移[6,7]的位置。

charc;〃第四個,自身長為1,放在[8]的位置。

);

這個類實際占據(jù)的內存空間是9字節(jié)

類之間的對齊,是按照類內部最大的成員的長度,和#pragmapack規(guī)定的

值之中較小的一個對齊的。

所以這個例子中,類之間對齊的長度是min(sizeof(int),4),也就是4。

9按照4字節(jié)圓整的結果是12,所以sizeof(TestB)是12。

如果

Spragmapack(2)

classTestB

(

public:

intaa;〃第一個成員,放在[0,3]偏移的位置,

chara;〃第二個成員,自身長為1,^pragmapack(4),取小值,

也就是1,所以這個成員按一字節(jié)對齊,放在偏移[4]的位置。

shortb;〃第三個成員,自身長2,弁pragmapack(4),取2,按

2字節(jié)對齊,所以放在偏移[6,7]的位置。

charc;〃第四個,自身長為1,放在[8]的位置。

);

〃可以看出,上面的位置完全沒有變化,只是類之間改為按2字節(jié)對齊,9

按2圓整的結果是10。

〃所以sizeof(TestB)是10。

最后看原貼:

現(xiàn)在去掉第一個成員變量為如下代碼:

Spragmapack(4)

classTestC

public:

chara;〃第一個成員,放在[0]偏移的位置,

shortb;〃第二個成員,自身長2,Spragmapack(4),取2,按2

字節(jié)對齊,所以放在偏移⑵3]的位置。

charc;〃第三個,自身長為1,放在[4]的位置。

);

//整個類的大小是5字節(jié),按照min(sizeof(short),4)字節(jié)對齊,也就是2

字節(jié)對齊,結果是6

//所以sizeof(TestC)是6。

感謝Michael提出疑問,在此補充:

當數(shù)據(jù)定義中出現(xiàn)—declspec(align。)時,指定類型的對齊長度還要用

自身長度和這里指定的數(shù)值比較,然后取其中較大的。最終類/結構的對齊長度

也需要和這個數(shù)值比較,然后取其中較大的。

可以這樣理解,―declspec(align())和#pragmapack是一■對兄弟,

前者規(guī)定了對齊的最小值,后者規(guī)定了對齊的最大值,兩者同時出現(xiàn)時,前者擁

有更高的優(yōu)先級。

_declspec(align())的一個特點是,它僅僅規(guī)定了數(shù)據(jù)對齊的位置,而

沒有規(guī)定數(shù)據(jù)實際占用的內存長度,當指定的數(shù)據(jù)被放置在確定的位置之后,其

后的數(shù)據(jù)填充仍然是按照#pragmapack規(guī)定的方式填充的,這時候類/結構的實

際大小和內存格局的規(guī)則是這樣的:

在—declspec(align())之前,數(shù)據(jù)按照#pragmapack規(guī)定的方式填充,

如前所述。當遇到_declspec(alignO)的時候,首先尋找距離當前偏移向后

最近的對齊點(滿足對齊長度為max(數(shù)據(jù)自身長度,指定值)),然后把被指定

的數(shù)據(jù)類型從這個點開始填充,其后的數(shù)據(jù)類型從它的后面開始,仍然按照

^pragmapack填充,直到遇到下一個_declspec(align())。

當所有數(shù)據(jù)填充完畢,把結構的整體對齊數(shù)值和—declspec(alignO)規(guī)

定的值做比較,取其中較大的作為整個結構的對齊長度。

特別的,當—declspec(alignO)指定的數(shù)值比對應類型長度小的時候,

這個指定不起作用。

二、1,比如:

struct{

shortal;

shorta2;

shorta3;

}A;

struct{

longal;

shorta2;

}B;

sizeof(A)=6,sizeof(B)=8,為什么?

注:sizeof(short)=2,sizeof(long)=4

因為:“成員對齊有一個重要的條件,即每個成員按自己的方式對齊.其對齊的規(guī)則是,每個成員

按其類型的對齊參數(shù)(通常是這個類型的大小)和指定對齊參數(shù)(這里默認是8字節(jié))中較小的一

個對齊.并且結構的長度必須為所用過的所有對齊參數(shù)的整數(shù)倍,不夠就補空字節(jié).”(引用)

結構體A中有3個short類型變量,各自以2字節(jié)對齊,結構體對齊參數(shù)按默認的8字節(jié)對齊,

則al,a2,a3都取2字節(jié)對齊,則sizeof(A)為6,其也是2的整數(shù)倍;

B中al為4字節(jié)對齊,a2為2字節(jié)對齊,結構體默認對齊參數(shù)為8,則al取4字節(jié)對齊,a2

取2字節(jié)對齊,結構體大小6字節(jié),6不為4的整數(shù)倍,補空字節(jié),增到8時,符合所有條件,

則sizeof(B)為8;

可以設置成對齊的

#pragmapack(l)

#pragmapack(push)

#pragmapack(l)

struct{

shortal;

shorta2;

shorta3;

}A;

struct{

longal;

shorta2;

}B;

ttpragmapack(pop)結果為sizeof(A)=6,sizeof(B)=6

2,又如:

#pragmapack(8)

structSI{

chara;

longb;

);

structS2{

charc;

structSId;

longlonge;

);

^pragmapack()

sizeof(S2)結果為24.

成員對齊有一個重要的條件,即每個成員分別對齊.即每個成員按自己的方式對齊.

也就是說上面雖然指定了按8字節(jié)對齊,但并不是所有的成員都是以8字節(jié)對齊.其對齊的規(guī)則

是,每個成員按其類型的對齊參數(shù)(通常是這個類型的大?。┖椭付▽R參數(shù)(這里是8字節(jié))中較

小的一個對齊.并且結構的長度必須為所用過的所有對齊參數(shù)的整數(shù)倍,不夠就補空字節(jié).

S1中,成員a是1字節(jié)默認按1字節(jié)對齊,指定對齊參數(shù)為8,這兩個值中取1,a按1字節(jié)對齊;

成員b是4個字節(jié),默認是按4字節(jié)對齊,這時就按4字節(jié)對齊,所以sizeof($1)應該為8;

S2中,c和S1中的a一樣,按1字節(jié)對齊,而d是個結構,它是8個字節(jié),它按什么對齊呢?對于

結構來說,它的默認對齊方式就是它的所有成員使用的對齊參數(shù)中最大的一個,S1的就是4.所

以,成員d就是按4字節(jié)對齊.成員e是8個字節(jié),它是默認按8字節(jié)對齊,和指定的一樣,所以它

對到8字節(jié)的邊界上,這時,已經使用了12個字節(jié)了,所以又添加了4個字節(jié)的空,從第16個字

節(jié)開始放置成員e.這時,長度為24,已經可以被8(成員e按8字節(jié)對齊)整除.這樣,一共使用了

24個字節(jié).

ab

S1的內存布局:1***,1111,

cSI.aSI.be

S2的內存布局:1***,1***,1111,****11111111

這里有三點很重要:

1.每個成員分別按自己的方式對齊,并能最小化長度

2.復雜類型(如結構)的默認對齊方式是它最長的成員的對齊方式,這樣在成員是復雜類型時,可

以最小化長度

3.對齊后的長度必須是成員中最大的對齊參數(shù)的整數(shù)倍,這樣在處理數(shù)組時可以保證每一項都邊

界對齊

補充一下,對于數(shù)組,比如:

chara[3];這種,它的對齊方式和分別寫3個char是一樣的.也就是說它還是按1個字節(jié)對齊.

如果寫:typedefcharArray3[3];

Array3這種類型的對齊方式還是按1個字節(jié)對齊,而不是按它的長度.

不論類型是什么,對齊的邊界一定是1,2,4,8,16,32,64....中的一個.

字節(jié)對齊詳解

為什么要對齊?

現(xiàn)代計算機中內存空間都是按照byte劃分的,從理論上講似乎對任何類型

的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經

常在特定的內存地址訪問,這就需要各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排

列,而不是順序的一個接一個的排放,這就是對齊。

對齊的作用和原因:各個硬件平臺對存儲空間的處理上有很大的不同。一些

平臺對某些特定類型的數(shù)據(jù)只能從某些特定地址開始存取。比如有些架構的CPU

在訪問一個沒有進行對齊的變量的時候會發(fā)生錯誤,那么在這種架構下編程必

須保證字節(jié)對齊.其他平臺可能沒有這種情況,但是最常見的是如果不按照適合

其平臺要求對數(shù)據(jù)存放進行對齊,會在存取效率上帶來損失。比如有些平臺每

次讀都是從偶地址開始,如果一個int型(假設為32位系統(tǒng))如果存放在偶地

址開始的地方,那么一個讀周期就可以讀出這32bit,而如果存放在奇地址開

始的地方,就需要2個讀周期,并對兩次讀出的結果的高低字節(jié)進行拼湊才能得

到該32bit數(shù)據(jù)。顯然在讀取效率上下降很多。

二.字節(jié)對齊對程序的影響:

先讓我們看兒個例子吧(32bit,x86環(huán)境,gcc編譯器):

設結構體如下定義:

structA

inta;

charb;

shortc;

);

structB

{

charb;

inta;

shortc;

);

現(xiàn)在已知32位機器上各種數(shù)據(jù)類型的長度如下:

char:1(有符號無符號同)

short:2(有符號無符號同)

int:4(有符號無符號同)

long:4(有符號無符號同)

float:4double:8

那么上面兩個結構大小如何呢?

結果是:

sizeof(strcutA)值為8

sizeof(structB)的值卻是12

結構體A中包含了4字節(jié)長度的int一個,1字節(jié)長度的char一個和2字節(jié)長

度的short型數(shù)據(jù)一個,B也一樣;按理說A,B大小應該都是7字節(jié)。

之所以出現(xiàn)上面的結果是因為編譯器要對數(shù)據(jù)成員在空間上進行對齊。上面是按

照編譯器的默認設置進行對齊的結果,那么我們是不是可以改變編譯器的這種默

認對齊設置呢,當然可以.例如:

#pragmapack(2)/*指定按2字節(jié)對齊*/

structC

(

charb;

inta;

shortc;

);

#pragmapack()/*取消指定對齊,恢復缺省對齊*/

sizeof(structC)值是8。

修改對齊值為1:

#pragmapack(1)/*指定按1字節(jié)對齊*/

structD

{

charb:

inta;

shortc;

);

#pragmapack()/*取消指定對齊,恢復缺省對齊*/

sizeof(structD)值為7。

后面我們再講解#pragmapack。的作用.

三.編譯器是按照什么樣的原則進行對齊的?

先讓我們看四個重要的基本概念:

1.數(shù)據(jù)類型自身的對齊值:

對于char型數(shù)據(jù),其自身對齊值為1,對于short型為2,對于

int,float,double類型,其自身對齊值為4,單位字節(jié)。

2.結構體或者類的自身對齊值:其成員中自身對齊值最大的那個值。

3.指定對齊值:Spragmapack(value)時的指定對齊值value。

4.數(shù)據(jù)成員、結構體和類的有效對齊值:自身對齊值和指定對齊值中小的那個值。

有了這些值,我們就可以很方便的來討論具體數(shù)據(jù)結構的成員和其自身的對齊

方式。有效對齊值N是最終用來決定數(shù)據(jù)存放地址方式的值,最重要。有效對齊

N,就是表示“對齊在N上”,也就是說該數(shù)據(jù)的“存放起始地址潁=0〃.而數(shù)據(jù)

結構中的數(shù)據(jù)變量都是按定義的先后順序來排放的。第一個數(shù)據(jù)變量的起始地址

就是數(shù)據(jù)結構的起始地址。結構體的成員變量要對齊排放,結構體本身也要根

據(jù)自身的有效對齊值圓整(就是結構體成員變量占用總長度需要是對結構體有效

對齊值的整數(shù)倍,結合下面例子理解)。這樣就不能理解上面的兒個例子的值了。

例子分析:

分析例子B;

structB

{

charb;

inta;

shortc;

);

假設B從地址空間0x0000開始排放。該例子中沒有定義指定對齊值,在筆者環(huán)

境下,該值默認為4。第一個成員變量b的自身對齊值是1,比指定或者默認指

定對齊值4小,所以其有效對齊值為1,所以其存放地址0x0000符合

0x0000知=0.第二個成員變量a,其自身對齊值為4,所以有效對齊值也為4,所

以只能存放在起始地址為0x0004到0x0007這四個連續(xù)的字節(jié)空間中,復核

0x0004%4=0,且緊靠第一個變量。第三個變量c,自身對齊值為2,所以有效對齊

值也是2,可以存放在0x0008到0x0009這兩個字節(jié)空間中,符合0x0008%2=0。

所以從0x0000到0x0009存放的都是B內容。再看數(shù)據(jù)結構B的自身對齊值為

其變量中最大對齊值(這里是b)所以就是4,所以結構體的有效對齊值也是4。

根據(jù)結構體圓整的要求,0x0009到0x0000=10字節(jié),(10+2)%4=0。所以

OxOOOOA到OxOOOB也為結構體B所占用。故B從0x0000到OxOOOB共有12個字

節(jié),sizeof(structB)=12;其實如果就這?個就來說它已將滿足字節(jié)對齊了,因

為它的起始地址是0,因此肯定是對齊的,之所以在后面補充2個字節(jié),是因為編

譯器為了實現(xiàn)結構數(shù)組的存取效率,試想如果我們定義了?個結構B的數(shù)組,那

么第一個結構起始地址是0沒有問題,但是第二個結構呢?按照數(shù)組的定義,數(shù)組

中所有元素都是緊挨著的,如果我們不把結構的大小補充為4的整數(shù)倍,那么下

一個結構的起始地址將是OxOOOOA,這顯然不能滿足結構的地址對齊了,因此我

們要把結構補充成有效對齊大小的整數(shù)倍.其實諸如:對于char型數(shù)據(jù),其自身

對齊值為1,對于short型為2,對于int,float,double類型,其自身對齊值為

4,這些已有類型的自身對齊值也是基于數(shù)組考慮的,只是因為這些類型的長度

已知了,所以他們的自身對齊值也就已知了.

同理,分析上面例子C:

#pragmapack(2)/*指定按2字節(jié)對齊*/

structC

{

charb;

inta;

shortc;

);

#pragmapack()/*取消指定對齊,恢復缺省對齊*/

第一個變量b的自身對齊值為1,指定對齊值為2,所以,其有效對齊值為1,

假設C從0x0000開始,那么b存放在0x0000,符合0x0000%l=0;第二個變量,

自身對齊值為4,指定對齊值為2,所以有效對齊值為2,所以順序存放在0x0002、

0x0003、0x0004、0x0005四個連續(xù)字節(jié)中,符合0x0002%2=0。第三個變量c

的自身對齊值為2,所以有效對齊值為2,順序存放

在0x0006、0x0007中,符合0x0006%2=0o所以從0x0000到0x00007共八字節(jié)

存放的是C的變量。又C的自身對齊值為4,所以C的有效對齊值為2。又8%2=0,C

只占用0x0

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論