• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 297,  comments - 15,  trackbacks - 0

            前向聲明:
                sizeof,一個(gè)其貌不揚(yáng)的家伙,引無數(shù)菜鳥竟折腰,小蝦我當(dāng)初也沒少犯迷糊,秉著“辛苦我一個(gè),幸福千萬人”的偉大思想,我決定將其盡可能詳細(xì)的總結(jié)一下。
                但當(dāng)我總結(jié)的時(shí)候才發(fā)現(xiàn),這個(gè)問題既可以簡單,又可以復(fù)雜,所以本文有的地方并不適合初學(xué)者,甚至都沒有必要大作文章。但如果你想“知其然,更知其所以然”的話,那么這篇文章對(duì)你或許有所幫助。
                菜鳥我對(duì)C++的掌握尚未深入,其中不乏錯(cuò)誤,歡迎各位指正啊

            1. 定義:
                sizeof是何方神圣sizeof乃C/C++中的一個(gè)操作符(operator)是也,簡單的說其作用就是返回一個(gè)對(duì)象或者類型所占的內(nèi)存字節(jié)數(shù)。

            MSDN上的解釋為:
            The sizeof keyword gives the amount of storage, in bytes,

            associated with a variable or a type (including aggregate

            types). This keyword returns a value of type size_t.
                其返回值類型為size_t,在頭文件stddef.h中定義。這是一個(gè)依賴于編譯系統(tǒng)的值,一般定義為
            typedef unsigned int size_t;
                世上編譯器林林總總,但作為一個(gè)規(guī)范,它們都會(huì)保證char、signed char和unsigned
            char的sizeof值為1,畢竟char是我們編程能用的最小數(shù)據(jù)類型。

            2. 語法:
                sizeof有三種語法形式,如下:
                1) sizeof( object ); // sizeof( 對(duì)象 );
                2) sizeof( type_name ); // sizeof( 類型 );
                3) sizeof object; // sizeof 對(duì)象;
            所以,
            int i;
            sizeof( i ); // ok
            sizeof i; // ok
            sizeof( int ); // ok
            sizeof int; // error
                既然寫法3可以用寫法1代替,為求形式統(tǒng)一以及減少我們大腦的負(fù)擔(dān),第3種寫法,忘掉它吧!實(shí)際上,sizeof計(jì)算對(duì)象的大小也是轉(zhuǎn)換成對(duì)對(duì)象類型的計(jì)算,也就是說,同種類型的不同對(duì)象其sizeof值都是一致的。這里,對(duì)象可以進(jìn)一步延伸至表達(dá)式,即sizeof可以對(duì)一個(gè)表達(dá)式求值,編譯器根據(jù)表達(dá)式的最終結(jié)果類型來確定大小,一般不會(huì)對(duì)表達(dá)式進(jìn)行計(jì)算。如:
            sizeof( 2 ); // 2的類型為int,所以等價(jià)于 sizeof( int );
            sizeof( 2 + 3.14 );  // 3.14的類型為double,2也會(huì)被提升成double類型,所以等價(jià)于 sizeof( double );

                sizeof也可以對(duì)一個(gè)函數(shù)調(diào)用求值,其結(jié)果是函數(shù)返回類型的大小,函數(shù)并不會(huì)被調(diào)用,我們來看一個(gè)完整的例子:
            char foo()
            {
                printf("foo() has been called.\n");
                return 'a';
            }
            int main()
            {
                size_t sz = sizeof( foo() ); // foo() 的返回值類型為char,所以sz = sizeof(char ),foo()并不會(huì)被調(diào)用
                printf("sizeof( foo() ) = %d\n", sz);
            }

                C99標(biāo)準(zhǔn)規(guī)定,函數(shù)、不能確定類型的表達(dá)式以及位域(bit-field)成員不能被計(jì)算sizeof值,即下面這些寫法都是錯(cuò)誤的:
                sizeof( foo );// error
                void foo2() { }
                sizeof( foo2() );// error
                struct S
                {
                    unsigned int f1 : 1;
                    unsigned int f2 : 5;
                    unsigned int f3 : 12;
                };
                sizeof( S.f1 );// error

            3. sizeof的常量性
                sizeof的計(jì)算發(fā)生在編譯時(shí)刻,所以它可以被當(dāng)作常量表達(dá)式使用,如:
            char ary[ sizeof( int ) * 10 ]; // ok
                最新的C99標(biāo)準(zhǔn)規(guī)定sizeof也可以在運(yùn)行時(shí)刻進(jìn)行計(jì)算,如下面的程序在Dev-C++中可以正確執(zhí)行:
            int n;
            n = 10; // n動(dòng)態(tài)賦值
            char ary[n]; // C99也支持?jǐn)?shù)組的動(dòng)態(tài)定義
            printf("%d\n", sizeof(ary)); // ok. 輸出10
                但在沒有完全實(shí)現(xiàn)C99標(biāo)準(zhǔn)的編譯器中就行不通了,上面的代碼在VC6中就通不過編譯。所以我們最好還是認(rèn)為sizeof是在編譯期執(zhí)行的,這樣不會(huì)帶來錯(cuò)誤,讓程序的可移植性強(qiáng)些。

            4. 基本數(shù)據(jù)類型的sizeof
                這里的基本數(shù)據(jù)類型指short、int、long、float、double這樣的簡單內(nèi)置數(shù)據(jù)類型,由于它們都是和系統(tǒng)相關(guān)的,所以在不同的系統(tǒng)下取值可能不同,這務(wù)必引起我們的注意,盡量不要在這方面給自己程序的移植造成麻煩。
                一般的,在32位編譯環(huán)境中,sizeof(int)的取值為4。

            5. 指針變量的sizeof
                學(xué)過數(shù)據(jù)結(jié)構(gòu)的你應(yīng)該知道指針是一個(gè)很重要的概念,它記錄了另一個(gè)對(duì)象的地址。既然是來存放地址的,那么它當(dāng)然等于計(jì)算機(jī)內(nèi)部地址總線的寬度。所以在32位計(jì)算機(jī)中,一個(gè)指針變量的返回值必定是4(注意結(jié)果是以字節(jié)為單位),可以預(yù)計(jì),在將來的64位系統(tǒng)中指針變量的sizeof結(jié)果為8。

            char* pc = "abc";
            int* pi;
            string* ps;
            char** ppc = &pc;
            void (*pf)();// 函數(shù)指針
            sizeof( pc ); // 結(jié)果為4
            sizeof( pi ); // 結(jié)果為4
            sizeof( ps ); // 結(jié)果為4
            sizeof( ppc ); // 結(jié)果為4
            sizeof( pf );// 結(jié)果為4
                指針變量的sizeof值與指針?biāo)傅膶?duì)象沒有任何關(guān)系,正是由于所有的指針變量所占內(nèi)存大小相等,所以MFC消息處理函數(shù)使用兩個(gè)參數(shù)WPARAM、LPARAM就能傳遞各種復(fù)雜的消息結(jié)構(gòu)(使用指向結(jié)構(gòu)體的指針)。

            6. 數(shù)組的sizeof
                數(shù)組的sizeof值等于數(shù)組所占用的內(nèi)存字節(jié)數(shù),如:
            char a1[] = "abc";
            int a2[3];
            sizeof( a1 ); // 結(jié)果為4,字符 末尾還存在一個(gè)NULL終止符
            sizeof( a2 ); // 結(jié)果為3*4=12(依賴于int)

                一些朋友剛開始時(shí)把sizeof當(dāng)作了求數(shù)組元素的個(gè)數(shù),現(xiàn)在,你應(yīng)該知道這是不對(duì)的,那么應(yīng)該怎么求數(shù)組元素的個(gè)數(shù)呢Easy,通常有下面兩種寫法:
            int c1 = sizeof( a1 ) / sizeof( char ); // 總長度/單個(gè)元素的長度
            int c2 = sizeof( a1 ) / sizeof( a1[0] ); // 總長度/第一個(gè)元素的長度

                寫到這里,提一問,下面的c3,c4值應(yīng)該是多少呢
            void foo3(char a3[3])
            {
                int c3 = sizeof( a3 ); // c3 ==
            }
            void foo4(char a4[])
            {
                int c4 = sizeof( a4 ); // c4 ==
            }
                也許當(dāng)你試圖回答c4的值時(shí)已經(jīng)意識(shí)到c3答錯(cuò)了,是的,c3!=3。這里函數(shù)參數(shù)a3已不再是數(shù)組類型,而是蛻變成指針,相當(dāng)于char* a3,為什么仔細(xì)想想就不難明白,我們調(diào)用函數(shù)foo1時(shí),程序會(huì)在棧上分配一個(gè)大小為3的數(shù)組嗎不會(huì)!數(shù)組是“傳址”的,調(diào)用者只需將實(shí)參的地址傳遞過去,所以a3自然為指針類型(char*),c3的值也就為4。

            7. 結(jié)構(gòu)體的sizeof
                這是初學(xué)者問得最多的一個(gè)問題,所以這里有必要多費(fèi)點(diǎn)筆墨。讓我們先看一個(gè)結(jié)構(gòu)體:
            struct S1
            {
                char c;
                int i;
            };
                問sizeof(s1)等于多少聰明的你開始思考了,char占1個(gè)字節(jié),int占4個(gè)字節(jié),那么加起來就應(yīng)該是5。是這樣嗎你在你機(jī)器上試過了嗎也許你是對(duì)的,但很可能你是錯(cuò)的!VC6中按默認(rèn)設(shè)置得到的結(jié)果為8。

                Why為什么受傷的總是我
                請(qǐng)不要沮喪,我們來好好琢磨一下sizeof的定義——sizeof的結(jié)果等于對(duì)象或者類型所占的內(nèi)存字節(jié)數(shù),好吧,那就讓我們來看看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上的說明:
            When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment.
                原來如此,這就是傳說中的字節(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值就增長了。
                讓我們交換一下S1中char與int的位置:
            struct S2
            {
                int i;
                char c;
            };
                看看sizeof(S2)的結(jié)果為多少,怎么還是8再看看內(nè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)需要說明:
                1) 前面不是說結(jié)構(gòu)體成員的地址是其大小的整數(shù)倍,怎么又說到偏移量了呢因?yàn)橛辛说?點(diǎn)存在,所以我們就可以只考慮成員的偏移量,這樣思考起來簡單。想想為什么。

                結(jié)構(gòu)體某個(gè)成員相對(duì)于結(jié)構(gòu)體首地址的偏移量可以通過宏offsetof()來獲得,這個(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ù)類型,這里所說的“數(shù)據(jù)寬度”就是指其sizeof的大小。由于結(jié)構(gòu)體的成員可以是復(fù)合類型,比如另外一個(gè)結(jié)構(gòu)體,所以在尋找最寬基本類型成員時(shí),應(yīng)當(dāng)包括復(fù)合類型成員的子成員,而不是把復(fù)合成員看成是一個(gè)整體。但在確定復(fù)合類型成員的偏移位置時(shí)則是將復(fù)合類型作為整體看待。
                這里敘述起來有點(diǎn)拗口,思考起來也有點(diǎn)撓頭,還是讓我們看看例子吧(具體數(shù)值仍以VC6為例,以后不再說明):
            struct S3
            {
                char c1;
                S1 s;
                char c2;
            };
                S1的最寬簡單成員的類型為int,S3在考慮最寬簡單類型成員時(shí)是將S1“打散”看的,所以S3的最寬簡單類型為int,這樣,通過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。

                通過上面的敘述,我們可以得到一個(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指令。它是用來調(diào)整結(jié)構(gòu)體對(duì)齊方式的,不同編譯器名稱和用法略有不同,VC6中通過#pragma pack實(shí)現(xiàn),也可以直接修改/Zp編譯開關(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),即是說,結(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)說過,位域成員不能單獨(dú)被取sizeof值,我們這里要討論的是含有位域的結(jié)構(gòu)體的sizeof,只是考慮到其特殊性而將其專門列了出來。
                C99規(guī)定int、unsigned int和bool可以作為位域類型,但編譯器幾乎都對(duì)此作了擴(kuò)展,允許其它類型類型的存在。使用位域的主要目的是壓縮存儲(chǔ),其大致規(guī)則為:
                1) 如果相鄰位域字段的類型相同,且其位寬之和小于類型的sizeof大小,則后面的字段將緊鄰前一個(gè)字段存儲(chǔ),直到不能容納為止;
                2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小,則后面的字段將從新的存儲(chǔ)單元開始,其偏移量為其類型大小的整數(shù)倍;
                3) 如果相鄰的位域字段的類型不同,則各編譯器的具體實(shí)現(xiàn)有差異,VC6采取不壓縮方式,Dev-C++采取壓縮方式;
                4) 如果位域字段之間穿插著非位域字段,則不進(jìn)行壓縮;
                5) 整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。

                還是讓我們來看看例子。
                示例1:
            struct BF1
            {
                char f1 : 3;
                char f2 : 4;
                char f3 : 5;
            };
                其內(nèi)存布局為:
            |_f1__|__f2__|_|____f3___|____|
            |_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
            0 3 7 8 1316
                位域類型為char,第1個(gè)字節(jié)僅能容納下f1和f2,所以f2被壓縮到第1個(gè)字節(jié)中,而f3只
            能從下一個(gè)字節(jié)開始。因此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;
            };

            posted on 2008-08-19 23:51 chatler 閱讀(134) 評(píng)論(0)  編輯 收藏 引用 所屬分類: C++_BASIS
            <2009年5月>
            262728293012
            3456789
            10111213141516
            17181920212223
            24252627282930
            31123456

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            国产精品美女久久久m| 久久精品国产亚洲精品2020| 久久精品a亚洲国产v高清不卡 | 久久精品国产日本波多野结衣| 2020最新久久久视精品爱| 久久影院综合精品| 亚洲αv久久久噜噜噜噜噜| 精品多毛少妇人妻AV免费久久| 青青久久精品国产免费看| 日批日出水久久亚洲精品tv| 久久国产精品波多野结衣AV| 久久久久久毛片免费看| 久久99精品久久久久久噜噜 | 精品久久久久久99人妻| 91久久福利国产成人精品| 国内精品久久久久久久影视麻豆 | 久久精品中文字幕一区| 88久久精品无码一区二区毛片 | 夜夜亚洲天天久久| 国内精品久久久久久久涩爱 | 丰满少妇人妻久久久久久| 国产午夜福利精品久久2021| 97久久天天综合色天天综合色hd| 精品久久久久久综合日本| 精品久久久久久国产三级| 亚洲国产精品综合久久一线| 97香蕉久久夜色精品国产| 日韩精品久久久肉伦网站| 91精品国产9l久久久久| 久久久久99精品成人片牛牛影视| 日本亚洲色大成网站WWW久久| 亚洲乱码中文字幕久久孕妇黑人| 国内精品久久久久影院优| 亚洲午夜久久影院| 久久久久久精品久久久久| 国内精品久久久久影院优| 色99久久久久高潮综合影院| 久久水蜜桃亚洲av无码精品麻豆 | 久久午夜福利电影| 人妻精品久久久久中文字幕一冢本| 久久91精品久久91综合|