1、聊一聊
? ??本文跟大家分享的是C語言中sizeof一些需要注意的知識點,一方面可以避免大家再次掉坑,另一方面可以拿去吹吹牛!
2、正確認識sizeof
? ??
? ? 首先大家需要明確,sizeof不是一個函數(shù)而是一個操作符,一些小伙伴經(jīng)常口頭上掛著"sizeof函數(shù)",這種說法是不正確的。
? ? 應(yīng)該也好理解,比如sizeof(int),里面并沒有傳遞實參,如果其為函數(shù)便不成立,所以sizeof僅僅只是一個操作符,繼續(xù)實驗一下:
參考demo:
1#include?
2#include?
3
4/***************************************
5?*?Fuction:?sizeof簡單測試?
6?*?Author:???(公眾號:最后一個bug)?
7?**************************************/
8int?main(int?argc,?char?*argv[])?{
9????int?a?=?5;?
10????printf("sizeof(char)=?%d\n",sizeof(char));
11????printf("sizeof(int)=?%d\n",sizeof(int));
12????printf("sizeof(float)=?%d\n",sizeof(float));
13????printf("sizeof(double)=?%d\n",sizeof(double));
14????printf("sizeof(a)=?%d\n",sizeof(a));
15????return?0;
16}
匯編與結(jié)果:
分析一下:
匯編中5個紅線標記處對應(yīng)著C語言中5個sizeof使用點,在最終的匯編代碼中并沒有看到sizeof的痕跡。
同時還可以確認一點的是sizeof在編譯階段就完成了轉(zhuǎn)化,所以經(jīng)常有小伙伴考慮到sizeof會不會很耗時間等等,從這里看它僅僅只是一個常量,對程序的運行影響與常量是一致的。
?
? ? 因為在不同的平臺或者是編譯器,這些基礎(chǔ)的數(shù)據(jù)類型所占用的內(nèi)存字節(jié)空間不一定是相同的。
? ? sizeof功能就是計算出數(shù)據(jù)類型在內(nèi)存空間所占的字節(jié)數(shù),這樣就增強了程序的可移植性,特別是當我們進行內(nèi)存拷貝的時候顯得尤為有用。
? ? 比如一個大型結(jié)構(gòu)體數(shù)據(jù)的內(nèi)存拷貝,當由于內(nèi)存數(shù)據(jù)類型發(fā)生變化,或者是數(shù)據(jù)對齊等等原因?qū)е略摻Y(jié)構(gòu)體所占內(nèi)存發(fā)生變化,如果你采用的memcpy函數(shù)指定的大小沒有跟隨發(fā)生改變,則會出現(xiàn)問題,如下面示例:
參考demo:
1#include?
2#include?
3#include?
4
5//采用默認對齊?
6typedef?struct?_tag_Test1
7{
8????char?Val1;
9????int??Val2;
10????char?Val3;??
11}sTest1;
12
13sTest1?stTest1={1,2,3};
14
15//采用一個字節(jié)對齊?
16#pragma?pack(1)
17typedef?struct?_tag_Test2
18{
19????char?Val1;
20????int??Val2;
21????char?Val3;??
22}sTest2;
23
24#pragma?pack(0)
25sTest2?stTest2={4,5,6};
26/***************************************
27?*?Fuction:?sizeof可移植性?
28?*?Author:???(公眾號:最后一個bug)?
29?**************************************/
30int?main(int?argc,?char?*argv[])?{
31
32????sTest1?stTest1_ds;
33????sTest2?stTest2_ds;?
34
35????printf("sizeof(sTest1)?=?%d\n",sizeof(sTest1));
36????printf("sizeof(sTest2)?=?%d\n",sizeof(sTest2));
37
38??//?memcpy(&stTest1_ds,&stTest1,12);??//12和6硬編程,可移植性不好?
39??//?memcpy(&stTest2_ds,&stTest2,6);
40
41????memcpy(&stTest1_ds,&stTest1,sizeof(sTest1));
42????memcpy(&stTest2_ds,&stTest2,sizeof(sTest2));
43
44????printf("%d??%d??%d\n",stTest1_ds.Val1,stTest1_ds.Val2,stTest1_ds.Val3);
45????printf("%d??%d??%d\n",stTest2_ds.Val1,stTest2_ds.Val2,stTest2_ds.Val3);
46
47????return?0;
48}
輸出結(jié)果:
分析一下:
上面bug菌為了解釋結(jié)構(gòu)體在不同平臺占用空間有所不同,通過設(shè)置結(jié)構(gòu)體對齊來進行了模擬(如何設(shè)置結(jié)構(gòu)體對其字節(jié)個數(shù)可要學(xué)會!),使用sizeof明顯要比硬編程的移植性更好。
結(jié)構(gòu)體對齊不太不理解的可以參考<聽說因為代碼沒"對齊"程序就奔了?(深度剖析)>。
? ? 在之前的文中bug菌講到了strlen返回的是size_t類型,其為無符號類型,參考<【C進階】一不小心就被"strlen"給坑了!>,那么sizeof編譯器會處理成什么類型呢? 不防做個實驗:
參考demo:
1#include?
2#include?
3
4/***************************************
5?*?Fuction:?sizeof類型測試?
6?*?Author:???(公眾號:最后一個bug)?
7?**************************************/
8int?main(int?argc,?char?*argv[])?{
9
10????int?i=?-1;
11
12????if(i?>?sizeof(int))
13????{
14????????printf("sizeof?采用無符號類型\n");???
15????}
16????else
17????{
18????????printf("sizeof?采用有符號類型\n");???
19????}?
20
21????return?0;
22}
輸出結(jié)果:
分析一下:
-1 > 4的結(jié)果是C語言進行了自動類型轉(zhuǎn)化,不理解的可參考<【重磅】“整形數(shù)”還真沒那么簡單(C語言版)>;
sizeof和strlen函數(shù)的返回類型一樣,都是size_t類型(可能有些平臺指定為unsigned int),而該類型一般定義無符號整形,這樣也就會出現(xiàn)如上的實驗現(xiàn)象,以后多加小心。其實也很好理解,它們都是計算數(shù)據(jù)長度的方法也就沒有必要使用有符號類型。
這里提到strlen與sizeof,也是經(jīng)常使用過程中容易混淆的,這兩者有些相似也有不同,不過大家只要從定義出發(fā)就沒有問題了,如下Demo:
1#include?
2#include?
3
4/***************************************
5?*?Fuction:?sizeof與strlen?
6?*?Author:???(公眾號:最后一個bug)?
7?**************************************/
8int?main(int?argc,?char?*argv[])?{
9
10????char?*?cbug?=?"bug";?
11
12????printf("sizeof(cbug)?=?%d\n",sizeof(cbug));?//?b?u?g?\n?
13
14????printf("strlen(cbug)?=?%d\n",strlen(cbug));?//?b?u?g?
15
16????return?0;
17}
? ? 這一點大家可能會疑惑,這里所說的單一不是說功能單一,而是sizeof只在編譯階段檢測并計算其后的類型,其他表達式均不處理,見代碼見真相 :
參考demo:
1#include?
2#include?
3
4/***************************************
5?*?Fuction:??Cal?
6?*?Author:???(公眾號:最后一個bug)?
7?**************************************/
8int?Cal(int?*param1,int?*param2,int?*result)
9{
10????*result?=?*param1?+?*param2;
11?????return?1;
12}?
13
14/***************************************
15?*?Fuction:?sizeof重點實例?
16?*?Author:???(公眾號:最后一個bug)?
17?**************************************/
18int?main(int?argc,?char?*argv[])?{
19
20????int?i?=?1;?
21????char?j?=?1;?
22????int?a?=?2;
23????int?b?=?2;
24????int?ret?=?0;
25
26????printf("sizeof(i++)??=?%d\n",sizeof(i++));??
27????printf("sizeof(++i)??=?%d\n",sizeof(++i));??
28????printf("sizeof?++i???=?%d\n",sizeof?++j);???
29
30????printf("sizeof(Cal)??=?%d\n",sizeof(Cal(&a,&b,&ret)));
31????printf("sizeof?Cal???=?%d\n",sizeof?Cal(&a,&b,&ret));?
32
33????printf("i????????????=?%d\n",i);
34????printf("j????????????=?%d\n",j);????
35????printf("ret??????????=?%d\n",ret);??
36????return?0;
37}
輸出結(jié)果:
分析一下:
通過上面的實驗大家可以發(fā)現(xiàn)sizeof后的表達式均沒有執(zhí)行,編譯器把sizeof修飾部分通過計算其類型占用空間大小直接替換。
所以很多小伙伴編碼比較隨意容易出現(xiàn)這種類型的bug,當然sizeof后面接具體的數(shù)據(jù)類型一定需要小括號,而是其他非void表達式均可以省略該小括號,上面的實例中為大家展示了。
? ? 最后兩個小細節(jié):
1 )?sizeof(數(shù)組名)和sizeof(指針)的差別。前者為總的數(shù)組字節(jié)個數(shù),而后者僅為平臺指針所占字節(jié)個數(shù)。
2 )?sizeof不能用來計算位域大小。其實也很好理解,sizeof僅僅只計算字節(jié)個數(shù),位域bit個數(shù)編譯器不識別。
前提條件
現(xiàn)以上內(nèi)容暫不考慮C99標準下的sizeof的使用情況。
由于在C99標準下存在不定長數(shù)組的使用,從而使得sizeof會在程序運行階段確定對應(yīng)的類型字節(jié)個數(shù)
5、結(jié)束語
? ? 本文到這里就結(jié)束了,sizeof理解好了其實并不難,就怕你閱讀一些反人類的代碼,從而造成理解上的困難!當然面試官也可能考你一波!
????好了,這里是公眾號:“最后一個bug”,一個為大家打造的技術(shù)知識提升基地。
推薦好文??點擊藍色字體即可跳轉(zhuǎn)
【開源】bug菌把"動態(tài)數(shù)字顯示"開源了!
【嵌入式】bug粉碎機之volatile的那些坑
【MCU】用stm32的UID給固件加密(重點在加密)
【硬核C進階】如何實現(xiàn) 萬能 "兩數(shù)交換" 宏 ?
?【進階】同事用#include"xxx.c"把我給驚呆了!!
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!