|
http://blog.csdn.net/zzran/article/details/8671645 2013 程序員在面試的時(shí)候經(jīng)常會(huì)碰到一些題目,給出一個(gè)結(jié)構(gòu)體,然后sizeof它一下,問(wèn)值是多少?比如給出下面這樣一個(gè)結(jié)構(gòu)體:
也許一些剛開(kāi)始學(xué)習(xí)c語(yǔ)言的同學(xué)就會(huì)毫不猶豫的把struct當(dāng)中每個(gè)變量所占用的空間相加,等到的結(jié)果是8。之后結(jié)果就錯(cuò)了。為什么呢,首先讓把他們的地址打印出來(lái)看個(gè)究竟,這是打印出來(lái)的結(jié)果:a=0x00000000,b=0x0000002,c=0x00000004,d=0x00000008. 很奇怪吧,不像我們預(yù)想的那樣,他們是按順序存儲(chǔ)的。這就涉及到一個(gè)內(nèi)存排列的問(wèn)題:內(nèi)存對(duì)齊。 首先解釋一下,為什么要內(nèi)存對(duì)齊呢,這和我們的處理器的特性有關(guān)系,總線讀取內(nèi)存總是從偶數(shù)字節(jié)開(kāi)始的,如果按照順序進(jìn)行排列的話,short b 排列的內(nèi)存地址應(yīng)該是:0x00000001,但是它需要占用兩個(gè)字節(jié)的內(nèi)存空間,如果它要在這個(gè)內(nèi)存地址開(kāi)始存放的話,需要被讀取兩次,而且讀取完成之后還要進(jìn)行內(nèi)存內(nèi)容拼接,才能完整的得到這個(gè)short型的變量。如果它在b=0x0000002這個(gè)地址開(kāi)始存放,那么只需要讀取一次而且不需要進(jìn)行內(nèi)存內(nèi)容的拼接。然后變量c開(kāi)始從b之后存放,地址是0x00000004,但是它只占了一個(gè)字節(jié),同樣的道理,剩余的空間不能用來(lái)存放,但是既然處理器是從偶數(shù)字節(jié)開(kāi)始讀取的,那么為什么d的開(kāi)始地址是0x00000008呢。別著急,還有一個(gè)規(guī)定,就是一個(gè)字也就是16bits,雙字(32bits)操作數(shù)如果跨越了4字節(jié)邊界,或者一個(gè)四字操作數(shù)跨越了8字節(jié)邊界被認(rèn)為是未對(duì)齊的。也就是說(shuō),如果d從0x00000006開(kāi)始存放的話,那他就要跨越0x00000008這個(gè)能整除4的邊界,故而需要兩次的內(nèi)存讀寫(xiě)??梢曰仡^看看short b,它是從0x00000002開(kāi)始存放的,但是它并沒(méi)有跨越0x00000004這個(gè)邊界值。 還可以舉出這樣一個(gè)例子:
還有一個(gè)比較特殊的情況,那就是char類型的變量比較隨和,因?yàn)樗?2位的處理器中就占有一個(gè)字節(jié),因此把它放在哪里它都不介意,也就是說(shuō),因?yàn)樗徽家粋€(gè)字節(jié),無(wú)論存放在哪里,都只需要讀取一次。 看這樣一個(gè)例子:
打印出來(lái)的結(jié)果是:a=0x00000000,b=0x0000002,c=0x00000003,d=0x00000008.
接下來(lái)說(shuō)一下 #pragma pack(n)這個(gè)預(yù)處理,主要的功能是改變內(nèi)存對(duì)齊方式的選項(xiàng),按照給定的n字節(jié)進(jìn)行內(nèi)存對(duì)齊。但是結(jié)構(gòu)體成員對(duì)齊的方式有一個(gè)很重要的特點(diǎn),就是最小原則。結(jié)構(gòu)體成員對(duì)齊的規(guī)則如下: 將自己的本身在內(nèi)存中實(shí)際占用的字節(jié)和當(dāng)前由#pragma pack(n)設(shè)定的n進(jìn)行比較,取其中最下的那個(gè)作為結(jié)構(gòu)體當(dāng)前成員的對(duì)齊方式,但是不影響其他結(jié)構(gòu)體成員的對(duì)齊方式。 舉個(gè)例子:
上面這個(gè)例子設(shè)定的內(nèi)存對(duì)齊方式是8字節(jié)對(duì)齊形式。那么我們看看結(jié)構(gòu)體test_st1,其中a所占內(nèi)存大小為1,和規(guī)定的比較,取最小的,故對(duì)齊方式為1字節(jié)對(duì)齊,對(duì)于成員b,因?yàn)樗加?個(gè)字節(jié),而規(guī)定的是8,所以取最小的,對(duì)齊方式為4字節(jié)對(duì)齊,就是從內(nèi)存地址可以整除4緊挨a存放的地址開(kāi)始存放b,可以得到結(jié)構(gòu)體的大小為8字節(jié)。 之后再來(lái)看看結(jié)構(gòu)體test_st2這個(gè)結(jié)構(gòu)體,c是本身是一個(gè)字節(jié),所以對(duì)齊方式是1,而st1是一個(gè)結(jié)構(gòu),那么這個(gè)結(jié)構(gòu)本身在其他結(jié)構(gòu)體中,對(duì)齊的方式是什么呢,是以結(jié)構(gòu)體的大小和給定的對(duì)齊方式做比較嗎?不對(duì),它的對(duì)齊方式是它成員變量中最大的那個(gè)成員變量所占的內(nèi)存空間和給定的值進(jìn)行比較,繼而,st1的對(duì)齊方式是4,它的起始地址是可以整除4的地方開(kāi)始。 對(duì)于d,因?yàn)樗加?個(gè)字節(jié)的內(nèi)存,所以它的對(duì)齊方式是8,c和st1用去了12個(gè)字節(jié),所以d從內(nèi)存地址可以整除8的地方開(kāi)始存放,所以這個(gè)test_st2結(jié)構(gòu)體的大小是24.給出一個(gè)完整的測(cè)試程序:
可以自行調(diào)試一下,看看內(nèi)存中他的內(nèi)存排列。
|
|
|
來(lái)自: 心不留意外塵 > 《數(shù)據(jù)類型與指針》