C/C++基礎之sizeof使用
在 C/C++ 中,sizeof() 是一個判斷數(shù)據(jù)類型或者表達式長度的運算符。
1、sizeof 定義sizeof 是 C/C++ 中的一個操作符(operator),返回一個對象或者類型所占的內存字節(jié)數(shù)。
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.
——來自MSDN
其返回值類型為 size_t ,在頭文件 stddef.h 中定義為: typedef unsigned int size_t;
從sizeof 的定義可以看出:sizeof 不是一個函數(shù),因為函數(shù)調用必須有一對括號。
- #include <stdio.h>
- int main(void)
- {
- int num = 97;
- printf("sizeof(num = 0)的值:%d\n",sizeof(num = 0));
- printf("num 的值:%d\n",num);
- return 0;
- }
運行結果為4,97;并不是4,0
說明:sizeof 不是標準意義上的一元操作符,不支持鏈式表達式,sizeof 作用域范圍內的語句不會編譯成機器碼,如 sizeof(num++) 中的 ++ 不執(zhí)行。sizeof 也不是函數(shù), sizeof 更像一個特殊的宏,在編譯階段求值。
2、sizeof 用法sizeof 有兩種語法形式,如下:
- sizeof(type_name); //sizeof(類型);
- sizeof (object); //或sizeof object 都屬于 sizeof對象;
所以:
- int i;
- sizeof(i); //合理
- sizeof i; //合理
- sizeof(int); //合理
- sizeof int; //不合理
- 對類型使用 sizeof 時,sizeof type_name 是非法的,必須寫為 sizeof(type_name);
- 無論是對對象還是類型取值,sizeof () 這種形式都是對的;
1)基本數(shù)據(jù)類型的 sizeof
這里的基本數(shù)據(jù)類型是指short、int、long、float、double這樣的簡單內置數(shù)據(jù)類型。
由于它們的內存大小是和系統(tǒng)相關的,所以在不同的系統(tǒng)下取值可能不同。
- #include <iostream>
- using namespace std;
- int main()
- {
- cout << "Size of char : " << sizeof(char) << endl;
- cout << "Size of int : " << sizeof(int) << endl;
- cout << "Size of short int : " << sizeof(short int) << endl;
- cout << "Size of long int : " << sizeof(long int) << endl;
- cout << "Size of float : " << sizeof(float) << endl;
- cout << "Size of double : " << sizeof(double) << endl;
- cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;
- return 0;
- }
在 32 位系統(tǒng)下內置數(shù)據(jù)類型與其 sizeof 運算結果如下:
- Size of char : 1
- Size of int : 4
- Size of short int : 2
- Size of long int : 4
- Size of float : 4
- Size of double : 8
- Size of wchar_t : 4
- unsigned 不影響內置類型 sizeof 的取值
2) 指針類型的 sizeof
指針主要用于存儲地址,前幾天文章C語言指針詳解提到過,指針變量的位寬等于機器字長,機器字長由 CPU 寄存器位數(shù)決定。在 32 位系統(tǒng)中,一個指針變量的返回值為 4 字節(jié), 64 位系統(tǒng)中指針變量的 sizeof 結果為 8 字節(jié)。
- char *p =”hello”;
- sizeof( p ); // 結果為4
- sizeof(*p); // 結果為1
- int *pi;
- sizeof( pi ); //結果為4
- sizeof(*pi); //結果為4
- char **pp = &p;
- sizeof( pp ); // 結果為4
- sizeof( *pp ); // 結果為4
- 指針變量的 sizeof 值與指針所指的對象類型沒有任何關系,與指針申請多少空間沒有關系,所有的指針變量所占內存大小均相等。
- 如果使用 32 位編譯器編譯得到程序是 32 位,那么在 64bits 系統(tǒng)下,指針變量大小仍然是 4 個字節(jié)。
3) 函數(shù)類型的 sizeof
函數(shù)類型以其返回類型作為自身類型,進行 sizeof 取值。
- void fun1()
- {
- }
- int fun2()
- {
- return 0;
- }
- double fun3()
- {
- return 0.0;
- }
- cout << sizeof(fun1()) << endl; //錯誤!無法對void類型使用sizeof
- cout << sizeof(fun2()) << endl; //fun2()返回值類型為int,輸出4
- cout << sizeof(fun3()) << endl; //fun3()返回值類型為double,輸出8
注意:不能對返回 void 函數(shù)和函數(shù)指針進行 sizeof 取值。
4) 數(shù)組類型的 sizeof
當 sizeof 作用于數(shù)組時,求取的是數(shù)組所有元素所占用的大小。
- int A[3][5];
- char c[]="abcdef";
- double*(*d)[3][6];
- cout<<sizeof(A)<<endl; //輸出60
- cout<<sizeof(A[4])<<endl; //輸出20
- cout<<sizeof(A[0][0])<<endl;//輸出4
- cout<<sizeof(c)<<endl; //輸出7
- cout<<sizeof(d)<<endl; //輸出4
- cout<<sizeof(*d)<<endl; //輸出72
- cout<<sizeof(**d)<<endl; //輸出24
- cout<<sizeof(***d)<<endl; //輸出4
- cout<<sizeof(****d)<<endl; //輸出8
A 的數(shù)據(jù)類型是 int[3][5] ,A[4] 的數(shù)據(jù)類型是 int[5],A[0][0]數(shù)據(jù)類型是 int 。所以:
- sizeof(A)==sizeof(int[3][5])==3*5*sizeof(int)==60
- sizeof(A[4])==sizeof(int[5])=5*sizeof(int)==20
- sizeof(A[0][0])==sizeof(int)==4
如果字符數(shù)組表示字符串,數(shù)組末自動插入 '\0',所以 c 的數(shù)據(jù)類型是 char[7] ,所以 sizeof(c)=sizeof(char[7])==7。
d 是一個很奇怪的定義,他表示一個指向 double*[3][6] 類型數(shù)組的指針。既然是指針,所以 sizeof(d) 就是4。
既然 d 是執(zhí)行 double*[3][6] 類型的指針, *d 就表示一個 double*[3][6] 的多維數(shù)組類型,因此 sizeof(*a)=3*6*sizeof(double*)=72 。
**d 表示一個 double*[6] 類型的數(shù)組,所以 sizeof(**d)=6*sizeof (double*)=24。
***d 表示其中的一個元素,也就是 double* ,所以 sizeof(***d)=4 。
****d 是一個 double ,所以 sizeof(****d)=sizeof(double)=8。
當數(shù)組作為函數(shù)形參時,下面輸出結果應該是多少呢?
- int GetStrLength(char str[])
- {
- return sizeof(str);
- }
- int main()
- {
- char szStr[] = "abcdef";
- cout<< GetStrLength() << endl;
- return 0;
- }
輸出不是 7 ,這里函數(shù)參數(shù) str[] 已不再是數(shù)組類型,而是蛻變成指針,我們調用函數(shù) GetStrLength() 時,程序會在棧上分配一個大小為 7 的數(shù)組嗎?不會!數(shù)組是“傳址”的,調用者只需將實參的地址傳遞過去,所以 str 自然為指針類型 (char*) ,輸出值為:4 。
- 數(shù)組的大小是各維數(shù)的乘積*數(shù)組元素的大小。
- 向函數(shù)形參傳遞數(shù)組,數(shù)組將會退化為指針,失去原來數(shù)組的特性。
5) 結構體類型的 sizeof
對于 struct 數(shù)據(jù)結構由 CPU 的對齊問題導致 struct 的大小變得比較復雜。具體可以查看以前的文章一文輕松理解內存對齊。
理論上,int 占 4byte , char 占一個 byte ,那么將它們放到一個結構體中應該占 4+1=5byte ;但是實際上,通過運行程序得到的結果是 8byte 。
- #include<stdio.h>
- struct{
- int x;
- char y;
- }Test;
- int main()
- {
- printf("%d\n",sizeof(Test)); // 輸出8不是5
- return 0;
- }
結構體的大小跟結構體成員對齊有密切關系,而并非簡單地等于各個成員的大小之和!比如對如下結構體兩個結構體 A、B 使用 sizeof 的結果分別是:16,24??梢钥闯?sizeof(B) 并不等于 sizeof(int)+sizeof(double)+sizeof(int)=16 。
- struct A
- {
- int num1;
- int num2;
- double num3;
- };
- struct B
- {
- int num1;
- double num3;
- int num2;
- };
結構體A和B中包含的成員都一樣,只不過順序不同而已,為什么其大小不一樣呢?要解釋這個問題,就要了解結構體成員對齊的規(guī)則。
- 結構體的大小等于結構體內最大成員大小的整數(shù)倍
- 結構體內的成員的首地址相對于結構體首地址的偏移量是其類型大小的整數(shù)倍,比如說 double 型成員相對于結構體的首地址的地址偏移量應該是 8 的倍數(shù)。
- 為了滿足規(guī)則 1 和 2 編譯器會在結構體成員之后進行字節(jié)填充!
從三個規(guī)則我們來看看為什么 sizeof(B) 等于 24 :首先假設結構體的首地址為0,第一個成員 num1 的首地址是 0 (滿足規(guī)則2),它的類型是 int ,因此它占用地址空間 0——3 。第二個成員 num3 是 double 類型,它占用 8 個字節(jié),由于之前的 num1 只占用了 4 個字節(jié),為了滿足規(guī)則 2 ,需要使用規(guī)則 3 在 num1 后面填充 4 個字節(jié)(4——7),使得 num3 的起始地址偏移量為 8 ,因此 num3 占用的地址空間是:8——15。第三個成員 num2 是 int 型,其大小為 4 ,由于 num1 和num3 一共占用了 16 個字節(jié),此時無須任何填充就能滿足規(guī)則 2。因此 num2 占用的地址空間是 16——19 。那么是不是結構體的總大小就是 0——19 共 20 個字節(jié)呢?請注意,別忘了規(guī)則1!由于結構體內最大成員是 double 占用 8 個字節(jié),因此最后還需要在 num2 后面填充 4 個字節(jié),使得結構體總體大小為 24 。
- struct S{ };
- sizeof(S); // 結果為1
對于一個空 struct 結構體取 sizeof 運算,運算結果為 1 并非 0 。因為編譯器為保證此空 struct 存在,專門分配一個字節(jié)。
如果存在結構體嵌套,無論內層還是外層均需要采用內存對齊。
6) 類的 sizeof
1.不含繼承和 static 成員變量的類。
在這種情況下,只需要考慮對齊方式即可。
- class A
- {
- public:
- int b;
- float c;
- char d;
- };
- class B
- {
- };
- int main(void)
- {
- cout << “sizeof(A) is ” << sizeof(A) << endl;
- //輸出結果為12
- cout << “sizeof(B) is ” << sizeof(B) << endl;
- //輸出結果為1
- return 0 ;
- }
- 空的 class 同樣也占用 1 個字節(jié)。
- 計算類對象的大小時,類成員函數(shù)不占用對象空間,只需要考慮類中數(shù)據(jù)成員的大小。
2.類中存在靜態(tài)成員變量
- class A
- {
- public:
- static int a;
- int b;
- float c;
- char d;
- };
- int main()
- {
- A object;
- cout << “sizeof(object) is ” << sizeof(object) << endl;
- //輸出結果為12
- return 0 ;
- }
因為在程序編譯期間,就已經(jīng)為 static 變量在靜態(tài)存儲區(qū)域分配了內存空間,并且這塊內存在程序的整個運行期間都存在。而每次聲明了類 A 的一個對象的時候,為該對象在堆上,根據(jù)對象的大小分配內存。
3.類中包含成員函數(shù)
- class A
- {
- public:
- static int a;
- int b;
- float c;
- char d;
- int add(int x,int y)
- {
- return x+y;
- }
- };
- int main()
- {
- A object;
- cout << “sizeof(object) is ” << sizeof(object) << endl;
- b = object.add(3,4);
- cout << “sizeof(object) is ” << sizeof(object) << endl;
- //輸出結果為12
- return 0 ;
- }
因為只有非靜態(tài)類成員變量在新生成一個object的時候才需要自己的副本。所以每個非靜態(tài)成員變量在生成新object需要內存,而function是不需要的。
3、sizeof 與 strlen 區(qū)別
- sizeof 是一個操作符,strlen 是庫函數(shù)。
- sizeof 的參數(shù)可以是數(shù)據(jù)的類型,也可以是變量,而 strlen 只能以結尾
- 編譯器在編譯時就計算出了 sizeof 的結果,而 strlen 函數(shù)必須在運行時才能計算出來。并且 sizeof 計算的是數(shù)據(jù)類型占內存的大小,而 strlen 計算的是字符串實際的長度。
- 數(shù)組做 sizeof 的參數(shù)不退化,傳遞給 strlen 就退化為指針了。如:
- int ss[20]="0123456789";
- sizeof(ss)=80, //ss表示在內存中的大小,20*4。
- strlen(ss) //錯誤,strlen的參數(shù)只能是char*,且必須是以“\0”結尾的。
- char *ss="0123456789";
- sizeof(ss)=4, //ss是指向字符串常量的字符指針。
- sizeof(*ss)=1, // *ss是第一個字符。
參考資料
- https://www.cnblogs.com/Western-Trail/p/10326180.html
- 《C/C++實踐進階之道》