補遺篇之sizeof

    sizeofC的單目運算符,它給出操作數存儲所需的字節數,其操作數可以是表達式或類型名。sizeof()是運算符,不是函數,沒有函數調用開銷。其結果是在編譯階段由編譯器自動給出,相當於常數,不象函數那樣在運行時纔得到返回值

    sizeof常用來屏蔽同一類型變量在不同平臺上佔用不同空間的問題,能提高C程序的可移植性。不懂得利用sizeof就出現int *p = malloc(800)

    問:爲什麼直接分配800字節?  答:因爲要分配200個整數的空間

    問:爲什麼200整數就是800字節?答:因爲在XX平臺上整數是4字節,200個就是800字節

    問:那其他平臺上整數也是4字節麼?如果不是怎麼辦?答:………

    改爲int *p=malloc(sizeof(int)*NUM);,所有平臺表達形式就一致了,數據類型的平臺差異就由編譯器通過sizeof屏蔽(不同平臺的編譯器負責給出正確的sizeof(類型)值)。然而或許由於權限給了編譯器,sizeof操作數覆蓋面太寬,增加了很多語法花樣。其實,知道在哪用sizeof更有實際意義,本來就是編譯器自動處理,又何必人爲摻和。下面內容適合應付C筆試,很多考官喜歡在這裏作文章。

sizeof禁區

sizeof不能用於函數,不完全類型或位字段。不完全類型指存儲空間未知的數據類型,如不定長數組、void型等。所以char char_v[]sizeof(char_v)sizeof(void)都不正確。

基本數據類型與指針

    操作數爲基本類型或一般變量時,sizeof得到類型或變量bytes,如int16位系統佔2bytes32位系統4bytes。操作數是類型須加括弧,變量名可加可不加,如:

    char c;

    sizeof c;//變量名可不加(),同樣得到變量類型所佔bytes,這裏等同於sizeof(char)

    操作數爲指針時sizeof得到指針型所佔bytes,如32位系統4bytes。但指針牽涉到其所指內存變量的類型,易概念混淆。如:

    int* a//sizeof(a)4,沒異議,但不同人所指的4意義不一定相同哦

    char *a; //sizeof(a)多少呢?答1就露餡了,char是誰的類型,sizeof(a)求的又是什麼類型,正確答案仍是4。不明白就看看前文指針類型的含義。

數組

    sizeof(數組)得到數組總bytes。結論簡單,但前提要確定操作數是否爲數組型。如char array[10],不同表達式中,array有時代表類型char [10]的數組;有時僅代表0號元素地址值:

    sizeof(array)中,array代表數組型,sizeof得到數組bytes10

    sizeof(*array)中,array是地址,*array是數組0號元素的值。所以sizeof(*array)得到數組單元的byte,即sizeof(char),爲1

    sizeof(array+n)中(n=0,1,...),array還是地址,array+n也是地址,所以sizeof(array+n)得到指針大小,相當於sizeof(char *),爲4

    此外數組作參數傳進函數時,編譯器傳遞數組首地址,所以函數內的sizeof(array)也取指針大小,如:

    int func(char array[10]){     return sizeof(array); //數組參數退化爲地址,sizeof(array)=4       }

struct/union

    按理說sizeofstruct大小隻需依次累加所有member所佔bytessizeofunion則取最大member所佔bytes。但如考慮內存對齊限制,情況複雜了。某些平臺編譯器會"對齊"存放,struct/union對齊可能包括:struct成員變量地址相對struct起始地址的偏移須爲該變量自身佔用bytes的倍數;struct/union總大小爲最大成員所佔bytes的倍數。舉例:

    struct MyStruct

    {

      double da;

      char ca;

      int ia;

   };

    編譯器先爲成員da分配空間,佔用sizeof(double)=8字節,其起始地址和結構起始地址相同;然後爲ca分配空間,這時可分配地址相對結構起始的offset爲8,滿足sizeof(char)=1倍數,所以ca放在offset 8bytes,自身佔1byte;接着ia,此時offset爲9,不是sizeof(int)=4的倍數,編譯器自動填充3bytes對齊,offset變爲12,可被4整除,ia存放在offset12處,佔4bytes。至此所有member分配完,總size爲8+1+3+4=16,剛好是結構的字節邊界(結構中最大變量所佔字節數sizeof(double)=8的倍數),不需再填充。所以最終結構大小爲16,其中3bytes是編譯器自動填充的空白字節。

    交換一下成員變量的位置,變爲:

    struct MyStruct

    {

      char ca;

      double da;

      int ia;

    };

    這時分配過程爲:ca offset爲0,滿足對齊,佔1byteda初始offset爲1,不是sizeof(double)=8的倍數,填充7bytes對齊,保存在offset爲8處,自身佔8bytesia初始offset爲16,是sizeof(int)=4倍數,不需填充,存放在offset爲16處,自身佔4bytes。至此所有member都分配了空間,總size爲1+7+8+4=20。但20不能被dasizeof(double)=8整除,不滿足結構邊界對齊,需額外填4bytes,(20+4)/8才滿足。所以struct最終大小爲:1+7+8+4+4=24。其中7+4=11是編譯器填充的空白字節。

對於union型,sizeof以其最大成員的字節數爲基礎,邊界對齊填充後得到結果。如不考慮對齊,sizeof a11,如考慮對齊,爲16

  union a{

      int r;

      double e;

      char q[11];

    };

    以上structunion內存對齊和sizeof結果,只爲說明編譯器的處理策略,不同編譯器結果可能不同,只要知道有這麼回事就行了

    另外:sizeof計算C++結構或類時,靜態成員變量不影響結構或類的大小,因爲靜態變量的存儲位置與結構或類的實例地址無關。

strlensizeof

    計算字符串長度的strlensizeof在某些場合易混淆,這裏做個對比:

    1sizeof是運算符,編譯時由編譯器給出結果;strlen是函數,運行中調用後得到結果。

    2sizeof可用類型做操作數,strlen只能用char*型變量做參數,且char*所指內存必須是以'\0'結尾的字符串,不能是字符數組。

    3sizeof得到某類型實際佔用內存的大小,而strlen通過遍歷到字符串結尾的'\0',得到字符串中的字符總數。如:

    char str[20]="0123456789";

    char* ss = "0123456789";

    int a=strlen(str);          //a=10,運行時計算得字符串長度

   int b=sizeof(str);         //b=20,編譯期確定str數組實際佔用內存的大小

    int c=sizeof(ss)          //ss是指向字符串的指針,sizeof取指針所佔空間,4字節

    int d=sizeof(*ss)        //*ss是字符串第一個字符'0',所佔的內存是char型,1字節

    int e=strlen(ss)         //e =10,獲取字符串長度,一定要用 strlen

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章