小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

c語(yǔ)言之不再害怕sizeof(struct)

 心不留意外塵 2016-05-13

http://blog.csdn.net/zzran/article/details/8671645

2013

程序員在面試的時(shí)候經(jīng)常會(huì)碰到一些題目,給出一個(gè)結(jié)構(gòu)體,然后sizeof它一下,問(wèn)值是多少?比如給出下面這樣一個(gè)結(jié)構(gòu)體:


  1. struct test{  
  2.    char a;  
  3.    short b;  
  4.    char c;  
  5.    int d;  
  6. };   

也許一些剛開(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è)例子:

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. struct test{  
  5.     char a;  
  6.     char b;  
  7.     char c;  
  8.     short d;  
  9.     int e;  
  10. };  
  11.   
  12. int main() {  
  13.     struct test t;  
  14.     int x1 = (unsigned int)(void*)&t.a - (unsigned int)(void*)&t;  
  15.     int x2 = (unsigned int)(void*)&t.b - (unsigned int)(void*)&t;  
  16.     int x3 = (unsigned int)(void*)&t.c - (unsigned int)(void*)&t;  
  17.     int x4 = (unsigned int)(void*)&t.d - (unsigned int)(void*)&t;  
  18.     int x5 = (unsigned int)(void*)&t.e - (unsigned int)(void*)&t;  
  19.     printf("a=0x%p,b=0x%p,c=0x%p,d=0x%p,e=0x%p", x1, x2, x3, x4, x5);  
  20.     cin.get();  
  21. }  
可以看看打印出來(lái)的結(jié)果, d的其實(shí)地址是0x00000004, 因?yàn)槿绻鼜?x00000003開(kāi)始存放的話,那就它跨越了邊界。

還有一個(gè)比較特殊的情況,那就是char類型的變量比較隨和,因?yàn)樗?2位的處理器中就占有一個(gè)字節(jié),因此把它放在哪里它都不介意,也就是說(shuō),因?yàn)樗徽家粋€(gè)字節(jié),無(wú)論存放在哪里,都只需要讀取一次。

看這樣一個(gè)例子:

 

  1. struct test{  
  2.    short a;  
  3.    char b;  
  4.    char c  
  5.    int d;  
  6. }  

打印出來(lái)的結(jié)果是:a=0x00000000,b=0x0000002,c=0x00000003,d=0x00000008.


缺省的情況下,編譯器默認(rèn)把結(jié)構(gòu),棧中的成員數(shù)據(jù)進(jìn)行內(nèi)存對(duì)齊。這樣以浪費(fèi)內(nèi)存空間的方式節(jié)省總線運(yùn)作的代價(jià)。


接下來(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è)例子:

  1. #pragma pack(8)  
  2.   
  3. struct test_st1{  
  4.     char a;  
  5.     long b;  
  6. };  
  7.   
  8. struct test_st2{  
  9.     char c;  
  10.     struct test_st1 st1;  
  11.     long long d;  
  12. };  

上面這個(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è)試程序:

  1. #include<iostream>  
  2. using namespace std;  
  3.   
  4. #pragma pack(8)  
  5.   
  6. struct test_st1{  
  7.     char a;  
  8.     long b;  
  9. };  
  10.   
  11. struct test_st2{  
  12.     char c;  
  13.     struct test_st1 st1;  
  14.     long long d;  
  15. };  
  16.   
  17. int main() {  
  18.     struct test_st2 t;  
  19.     int x1 = (unsigned int)(void*)&t.c - (unsigned int)(void*)&t;  
  20.     int x2 = (unsigned int)(void*)&t.st1.a - (unsigned int)(void*)&t;  
  21.     int x3 = (unsigned int)(void*)&t.st1.b - (unsigned int)(void*)&t;  
  22.     int x4 = (unsigned int)(void*)&t.d - (unsigned int)(void*)&t;  
  23.     printf("a=0x%p,b=0x%p,c=0x%p,d=0x%p", x1, x2, x3, x4);  
  24.     cin.get();  
  25. }  

可以自行調(diào)試一下,看看內(nèi)存中他的內(nèi)存排列。

 


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多