王若
摘要:內(nèi)存地址對(duì)齊,是一種在計(jì)算機(jī)內(nèi)存中排列數(shù)據(jù)、訪問數(shù)據(jù)的一種方式。當(dāng)今的計(jì)算機(jī)在計(jì)算機(jī)內(nèi)存中讀寫數(shù)據(jù)時(shí)都是按字(word)大小塊來進(jìn)行操作的?;绢愋蛿?shù)據(jù)對(duì)齊就是數(shù)據(jù)在內(nèi)存中的偏移地址必須等于一個(gè)字(word)的倍數(shù),按這種存儲(chǔ)數(shù)據(jù)的方式,可以提升系統(tǒng)在讀取數(shù)據(jù)時(shí)的性能。有時(shí)候?yàn)榱藢?duì)齊數(shù)據(jù),可能必須在上一個(gè)數(shù)據(jù)結(jié)束和下一個(gè)數(shù)據(jù)開始的地方插入一些沒有用處字節(jié),這就是結(jié)構(gòu)體數(shù)據(jù)對(duì)齊。
關(guān)鍵詞:結(jié)構(gòu);數(shù)據(jù)
一、假設(shè)計(jì)算機(jī)的字大小為4個(gè)字節(jié),因此變量在內(nèi)存中的首地址都是滿足4地址對(duì)齊,CPU只能對(duì)4的倍數(shù)的地址進(jìn)行讀取,而每次能讀取4個(gè)字節(jié)大小的數(shù)據(jù)
假設(shè)有一個(gè)整型的數(shù)據(jù)a的首地址不是4的倍數(shù),因此想讀取a的數(shù)據(jù),CPU要進(jìn)行兩次內(nèi)存讀取,而且還要對(duì)兩次讀取的數(shù)據(jù)進(jìn)行處理才能得到a的數(shù)據(jù),而一個(gè)程序的瓶頸往往不是CPU的速度,而是取決于內(nèi)存的帶寬,因?yàn)镃PU得處理速度要遠(yuǎn)大于從內(nèi)存中讀取數(shù)據(jù)的速度,因此減少對(duì)內(nèi)存空間的訪問是提高程序性能的關(guān)鍵[1]。從上例可以看出,采取內(nèi)存地址對(duì)齊策略是提高程序性能的關(guān)鍵。
二、結(jié)構(gòu)體默認(rèn)的規(guī)則
本文所述的環(huán)境均是在32位編譯器的編譯環(huán)境中,一般編譯器默認(rèn)對(duì)齊方式是4字節(jié)。
總結(jié)結(jié)構(gòu)體的數(shù)據(jù)對(duì)齊方式滿足條件:
1、結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除。2、結(jié)構(gòu)體每個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量(offset)都是成員自身大小的整數(shù)倍,如有需要編譯器會(huì)在成員之間加上填充字節(jié)。3、結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)。
三、手動(dòng)對(duì)齊方式
如果編譯器自動(dòng)實(shí)現(xiàn)結(jié)構(gòu)體對(duì)齊,我們就稱為自動(dòng)對(duì)齊,與之相反,使用#pragma進(jìn)行對(duì)齊的就是手動(dòng)對(duì)齊。
#pragma備用告訴編譯器,程序員自己希望的對(duì)齊方式。比如,雖然編譯器的默認(rèn)對(duì)齊方式是4,但是如果我們不希望按照4對(duì)齊,而是希望是8,這個(gè)時(shí)候就必須使用#pragma進(jìn)行手動(dòng)對(duì)齊了。
常用的設(shè)置手動(dòng)對(duì)齊的命令有兩種:第一種是#pragmapack(),這種就是設(shè)置編譯器1字節(jié)對(duì)齊,不過也可以認(rèn)為是設(shè)置為不對(duì)齊或者取消對(duì)齊;第二種是
#pragmapack(4),這個(gè)括號(hào)中的數(shù)字表示希望以多少字節(jié)進(jìn)行對(duì)齊。
我們需要#prgamapack(n)開頭,以#pragmapack()結(jié)尾,定義一個(gè)區(qū)間,這個(gè)區(qū)間內(nèi)的對(duì)齊參數(shù)就是n。
舉例說明
(一)自動(dòng)對(duì)齊方式或者是默認(rèn)4字節(jié)對(duì)齊
分析代碼:根據(jù)基本數(shù)據(jù)類型對(duì)齊規(guī)則可知,c(字節(jié)),i(4字節(jié)),d(8字節(jié)),b(2字節(jié))。是不是結(jié)果就是1+4+8+2呢?很明顯不是,c是首元素,不需要對(duì)齊,但是后面的就需要對(duì)齊了,i是4字節(jié),但是它的起始偏移量只有1字節(jié),不能整除4,因此就在c后面再加3個(gè)字節(jié),當(dāng)遇到d時(shí),由于之前的偏移量就是8,所以不需要偏移,在b之前有16字節(jié),這時(shí)也不需要偏移就是直接加上2。所以最后結(jié)果就是1+3+4+8+2=20,對(duì)不對(duì)呢?其實(shí)是不對(duì)的,因?yàn)?8不是默認(rèn)對(duì)齊4的整數(shù)倍,還需要在后面補(bǔ)充2字節(jié)。一共就是20字節(jié)。
分析:該結(jié)果就是24字節(jié),分析同上,但是在最后一步不一樣,對(duì)齊是8字節(jié),所以在b后面還需要添加6字節(jié)。最終結(jié)果就是24.
四、結(jié)語
需要字節(jié)對(duì)齊的根本原因在于CPU訪問數(shù)據(jù)的效率問題。因?yàn)橛?jì)算機(jī)可以處理數(shù)據(jù)位數(shù)都是確定的,這時(shí)候就說明它一次性只能處理確定位數(shù)的數(shù)據(jù),但是當(dāng)認(rèn)為造成該數(shù)據(jù)不在計(jì)算機(jī)一次性可訪問的范圍內(nèi)的時(shí)候,計(jì)算機(jī)就會(huì)按照一定的優(yōu)化方法來處理,這樣是更加方便和快捷的處理數(shù)據(jù)。也就是強(qiáng)制的要求一來簡化了處理器與內(nèi)存之間傳輸系統(tǒng)的設(shè)計(jì),二來可以提升讀取數(shù)據(jù)的速度。
參考文獻(xiàn)
[1] 陳榮,蔡志勇,胡保安. 基于嵌入式操作系統(tǒng)VxWorks數(shù)據(jù)采集系統(tǒng)軟件設(shè)計(jì)[J]. 科技廣場,2005,(06):82-84.