sizeof是C的單目運算符,它給出操作數存儲所需的字節數,其操作數可以是表達式或類型名。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,如int在16位系統佔2bytes,32位系統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得到數組總bytes,即10。
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
按理說sizeof求struct大小隻需依次累加所有member所佔bytes;sizeof求union則取最大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,滿足對齊,佔1byte;da初始offset爲1,不是sizeof(double)=8的倍數,填充7bytes對齊,保存在offset爲8處,自身佔8bytes;ia初始offset爲16,是sizeof(int)=4倍數,不需填充,存放在offset爲16處,自身佔4bytes。至此所有member都分配了空間,總size爲1+7+8+4=20。但20不能被da的sizeof(double)=8整除,不滿足結構邊界對齊,需額外填4bytes,(20+4)/8才滿足。所以struct最終大小爲:1+7+8+4+4=24。其中7+4=11是編譯器填充的空白字節。
對於union型,sizeof以其最大成員的字節數爲基礎,邊界對齊填充後得到結果。如不考慮對齊,sizeof a爲11,如考慮對齊,爲16。
union a{
int r;
double e;
char q[11];
};
以上struct和union內存對齊和sizeof結果,只爲說明編譯器的處理策略,不同編譯器結果可能不同,只要知道有這麼回事就行了。
另外:sizeof計算C++結構或類時,靜態成員變量不影響結構或類的大小,因爲靜態變量的存儲位置與結構或類的實例地址無關。
strlen與sizeof
計算字符串長度的strlen和sizeof在某些場合易混淆,這裏做個對比:
1.sizeof是運算符,編譯時由編譯器給出結果;strlen是函數,運行中調用後得到結果。
2.sizeof可用類型做操作數,strlen只能用char*型變量做參數,且char*所指內存必須是以'\0'結尾的字符串,不能是字符數組。
3.sizeof得到某類型實際佔用內存的大小,而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