乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      內(nèi)存對(duì)齊參考

       tom and jerry 2010-06-13
      結(jié)構(gòu)體的sizeof

      這是初學(xué)者問(wèn)得最多的一個(gè)問(wèn)題,所以這里有必要多費(fèi)點(diǎn)筆墨。讓我們先看一個(gè)結(jié)構(gòu)體:
      struct S1
      {
        char c;
        int i;
      };

      問(wèn)sizeof(s1)等于多少?聰明的你開(kāi)始思考了,char占1個(gè)字節(jié),int占4個(gè)字節(jié),那么加起來(lái)就應(yīng)該是5。是這樣嗎?你在你機(jī)器上試過(guò)了嗎?也許你是對(duì)的,但很可能你是錯(cuò)的!VC6中按默認(rèn)設(shè)置得到的結(jié)果為8。

      Why?為什么受傷的總是我?

      請(qǐng)不要沮喪,我們來(lái)好好琢磨一下sizeof的定義——sizeof的結(jié)果等于對(duì)象或者類型所占的內(nèi)存字節(jié)數(shù),好吧,那就讓我們來(lái)看看S1的內(nèi)存分配情況:
      S1 s1 = { 'a', 0xFFFFFFFF };

      定義上面的變量后,加上斷點(diǎn),運(yùn)行程序,觀察s1所在的內(nèi)存,你發(fā)現(xiàn)了什么?

      以我的VC6.0為例,s1的地址為0x0012FF78,其數(shù)據(jù)內(nèi)容如下:
      0012FF78: 61 CC CC CC FF FF FF FF

      發(fā)現(xiàn)了什么?怎么中間夾雜了3個(gè)字節(jié)的CC?看看MSDN上的說(shuō)明:
      When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.

      原來(lái)如此,這就是傳說(shuō)中的字節(jié)對(duì)齊??!一個(gè)重要的話題出現(xiàn)了。

      為什么需要字節(jié)對(duì)齊?計(jì)算機(jī)組成原理教導(dǎo)我們這樣有助于加快計(jì)算機(jī)的取數(shù)速度,否則就得多花指令周期了。為此,編譯器默認(rèn)會(huì)對(duì)結(jié)構(gòu)體進(jìn)行處理(實(shí)際上其它地方的數(shù)據(jù)變量也是如此),讓寬度為2的基本數(shù)據(jù)類型(short等)都位于能被2整除的地址上,讓寬度為4的基本數(shù)據(jù)類型(int等)都位于能被 4整除的地址上,以此類推。這樣,兩個(gè)數(shù)中間就可能需要加入填充字節(jié),所以整個(gè)結(jié)構(gòu)體的sizeof值就增長(zhǎng)了。

      讓我們交換一下S1中char與int的位置:
      struct S2
      {
        int i;
        char c;
      };

      看看sizeof(S2)的結(jié)果為多少,怎么還是8?再看看內(nèi)存,原來(lái)成員c后面仍然有3個(gè)填充字節(jié),這又是為什么???別著急,下面總結(jié)規(guī)律。

      字節(jié)對(duì)齊的細(xì)節(jié)和編譯器實(shí)現(xiàn)相關(guān),但一般而言,滿足三個(gè)準(zhǔn)則:
      1) 結(jié)構(gòu)體變量的首地址能夠被其最寬基本類型成員的大小所整除;
      2) 結(jié)構(gòu)體每個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量(offset)都是成員大小的整數(shù)倍,如有需要編譯器會(huì)在成員之間加上填充字節(jié)(internal adding);
      3) 結(jié)構(gòu)體的總大小為結(jié)構(gòu)體最寬基本類型成員大小的整數(shù)倍,如有需要編譯器會(huì)在最末一個(gè)成員之后加上填充字節(jié)(trailing padding)。

      對(duì)于上面的準(zhǔn)則,有幾點(diǎn)需要說(shuō)明:

      1) 前面不是說(shuō)結(jié)構(gòu)體成員的地址是其大小的整數(shù)倍,怎么又說(shuō)到偏移量了呢?因?yàn)橛辛说?點(diǎn)存在,所以我們就可以只考慮成員的偏移量,這樣思考起來(lái)簡(jiǎn)單。想想為什么。
      結(jié)構(gòu)體某個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量可以通過(guò)宏offsetof()來(lái)獲得,這個(gè)宏也在stddef.h中定義,如下:
      #define offsetof(s,m) (size_t)&(((s *)0)->m)

      例如,想要獲得S2中c的偏移量,方法為
      size_t pos = offsetof(S2, c); // pos等于4

      2) 基本類型是指前面提到的像char、short、int、float、double這樣的內(nèi)置數(shù)據(jù)類型,這里所說(shuō)的“數(shù)據(jù)寬度”就是指其sizeof的大小。由于結(jié)構(gòu)體的成員可以是復(fù)合類型,比如另外一個(gè)結(jié)構(gòu)體,所以在尋找最寬基本類型成員時(shí),應(yīng)當(dāng)包括復(fù)合類型成員的子成員,而不是把復(fù)合成員看成是一個(gè)整體。但在確定復(fù)合類型成員的偏移位置時(shí)則是將復(fù)合類型作為整體看待。

      這里敘述起來(lái)有點(diǎn)拗口,思考起來(lái)也有點(diǎn)撓頭,還是讓我們看看例子吧(具體數(shù)值仍以VC6為例,以后不再說(shuō)明):
      struct S3
      {
        char c1;
        S1 s;
        char c2
      };

      S1的最寬基本成員的類型為int,S3在考慮最寬基本類型成員時(shí)是將S1“打散”看的,所以S3的最寬基本類型為int,這樣,通過(guò)S3定義的變量,其存儲(chǔ)空間首地址需要被4整除,整個(gè)sizeof(S3)的值也應(yīng)該被4整除。

      c1的偏移量為0,s的偏移量呢?這時(shí)s是一個(gè)整體,它作為結(jié)構(gòu)體變量也滿足前面三個(gè)準(zhǔn)則,所以其大小為8,偏移量為4,c1與s之間便需要3個(gè)填充字節(jié),而c2與s之間就不需要了,所以c2的偏移量為12,算上c2的大小為13,13是不能被4整除的,這樣末尾還得補(bǔ)上3個(gè)填充字節(jié)。最后得到 sizeof(S3)的值為16。

      通過(guò)上面的敘述,我們可以得到一個(gè)公式:
      結(jié)構(gòu)體的大小等于最后一個(gè)成員的偏移量加上其大小再加上末尾的填充字節(jié)數(shù)目,即:
      sizeof( struct ) = offsetof( last item ) + sizeof( last item ) + sizeof( trailing padding )

      到這里,朋友們應(yīng)該對(duì)結(jié)構(gòu)體的sizeof有了一個(gè)全新的認(rèn)識(shí),但不要高興得太早,有一個(gè)影響sizeof的重要參量還未被提及,那便是編譯器的 pack指令。它是用來(lái)調(diào)整結(jié)構(gòu)體對(duì)齊方式的,不同編譯器名稱和用法略有不同,VC6中通過(guò)#pragma pack實(shí)現(xiàn),也可以直接修改/Zp編譯開(kāi)關(guān)。#pragma pack的基本用法為:#pragma pack( n ),n為字節(jié)對(duì)齊數(shù),其取值為1、2、4、8、16,默認(rèn)是8,如果這個(gè)值比結(jié)構(gòu)體成員的sizeof值小,那么該成員的偏移量應(yīng)該以此值為準(zhǔn),即是說(shuō),結(jié)構(gòu)體成員的偏移量應(yīng)該取二者的最小值,公式如下:
      offsetof( item ) = min( n, sizeof( item ) )

      再看示例:
      #pragma pack(push) // 將當(dāng)前pack設(shè)置壓棧保存
      #pragma pack(2) // 必須在結(jié)構(gòu)體定義之前使用
      struct S1
      {
        char c;
        int i;
      };
      struct S3
      {
        char c1;
        S1 s;
        char c2
      };
      #pragma pack(pop) // 恢復(fù)先前的pack設(shè)置

      計(jì)算sizeof(S1)時(shí),min(2, sizeof(i))的值為2,所以i的偏移量為2,加上sizeof(i)等于6,能夠被2整除,所以整個(gè)S1的大小為6。

      同樣,對(duì)于sizeof(S3),s的偏移量為2,c2的偏移量為8,加上sizeof(c2)等于9,不能被2整除,添加一個(gè)填充字節(jié),所以sizeof(S3)等于10。

      現(xiàn)在,朋友們可以輕松的出一口氣了,:)

      還有一點(diǎn)要注意,“空結(jié)構(gòu)體”(不含數(shù)據(jù)成員)的大小不為0,而是1。試想一個(gè)“不占空間”的變量如何被取地址、兩個(gè)不同的“空結(jié)構(gòu)體”變量又如何得以區(qū)分呢?于是,“空結(jié)構(gòu)體”變量也得被存儲(chǔ),這樣編譯器也就只能為其分配一個(gè)字節(jié)的空間用于占位了。如下:
      struct S5 { };
      sizeof( S5 ); // 結(jié)果為1

      8. 含位域結(jié)構(gòu)體的sizeof

      前面已經(jīng)說(shuō)過(guò),位域成員不能單獨(dú)被取sizeof值,我們這里要討論的是含有位域的結(jié)構(gòu)體的sizeof,只是考慮到其特殊性而將其專門列了出來(lái)。

      C99規(guī)定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對(duì)此作了擴(kuò)展,允許其它類型類型的存在。

      使用位域的主要目的是壓縮存儲(chǔ),其大致規(guī)則為:
      1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個(gè)字段存儲(chǔ),直到不能容納為止;
      2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲(chǔ)單元開(kāi)始,其偏移量為其類型大小的整數(shù)倍;
      3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;
      4) 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮;
      5) 整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。

      還是讓我們來(lái)看看例子。

      示例1:
      struct BF1
      {
        char f1 : 3;
        char f2 : 4;
        char f3 : 5;
      };

      其內(nèi)存布局為:
      | f1 | f2 | | f3 | |
      ---------------------------------
      | | | | | | | | | | | | | | | | |
      ---------------------------------
      0 3 7 8 13 16 (byte)

      位域類型為char,第1個(gè)字節(jié)僅能容納下f1和f2,所以f2被壓縮到第1個(gè)字節(jié)中,而f3只能從下一個(gè)字節(jié)開(kāi)始。因此sizeof(BF1)的結(jié)果為2。
      示例2:
      struct BF2
      {
        char f1 : 3;
        short f2 : 4;
        char f3 : 5;
      };

      由于相鄰位域類型不同,在VC6中其sizeof為6,在Dev-C++中為2。
      示例3:
      struct BF3
      {
        char f1 : 3;
        char f2;
        char f3 : 5;
      };

      非位域字段穿插在其中,不會(huì)產(chǎn)生壓縮,在VC6和Dev-C++中得到的大小均為3。

      9. 聯(lián)合體的sizeof

      結(jié)構(gòu)體在內(nèi)存組織上是順序式的,聯(lián)合體則是重疊式,各成員共享一段內(nèi)存,所以整個(gè)聯(lián)合體的sizeof也就是每個(gè)成員sizeof的最大值。結(jié)構(gòu)體的成員也可以是復(fù)合類型,這里,復(fù)合類型成員是被作為整體考慮的。

      所以,下面例子中,U的sizeof值等于sizeof(s)。
      union U
      {
        int i;
        char c;
        S1 s;
      };

        本站是提供個(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)論公約

        類似文章 更多