1. 程式人生 > >sizeof詳解,與strlen區別

sizeof詳解,與strlen區別

題綱:

一:定義

二:用法

三:常量性

四:各型別的sizeof

1 基本資料型別的sizeof

2 指標變數sizeof

3 陣列的sizeof

4 結構體的sizeof

5 聯合的sizeof

6 列舉的sizeof

7 類的sizeof

五:與strlen的區別


一:定義

首先說明,sizeof不是一個函式,是一個C/C++中的一個操作符,它判斷資料型別或者表示式佔用的記憶體位元組數,計算是在程式編譯時進行,而不是在程式執行的過程中才計算。返回值型別為:size_t。

MSDN:

The sizeof keyword gives the amount of storage, in bytes, associated with avariable or a type(including aggregate types). This keyword returns a value of type size_t.

二:用法

sizeof(型別名 或 變數名 或 陣列名 或 表示式);

sizeof 變數名;//不加括號

所以,sizeof有三種語法形式,如下:

sizeof(object);//sizeof(物件)
sizeof(type_name);//sizeof(型別)
sizeof object;//sizeof 物件

//所以,有以下判斷:
int i ;
sizeof(i);     //OK
sizeof i;      //OK
sizeof(int); //OK
sizeof int;  //error

實際上,sizeof計算物件的大小也是轉換成對物件型別的計算,也就是說,同種型別的不同物件其sizeof值都是一致的。這裡,物件可以進一步延伸至
表示式,即sizeof可以對一個表示式求值,編譯器根據表示式的最終結果型別來確定大小,一般不會對錶達式進行計算。如:
sizeof(2);//2的型別為int,所以等價於sizeof(int);
sizeof(2 + 3.14);//3.14的型別為double,2也會被提升成double型別,所以等價於sizeof(double);

sizeof也可以對一個函式呼叫求值,其結果是函式返回型別的大小,函式並不會被呼叫,我們來看一個完整的例子:
char foo()
{
   printf("foo() has been called.\n");
   return'a';
}
int main()
{
    size_t sz=sizeof(foo());
    //foo()的返回值型別為char,所以sz=sizeof(char),foo()並不會被呼叫
    printf("sizeof(foo())=%d\n",sz);
}

C99標準規定,函式、不能確定型別的表示式以及位域(bit-field)成員不能被計算sizeof值,即下面這些寫法都是錯誤的:
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

三:常量性

由於sizeof的計算髮生在編譯時刻,所以它可以被當作是常量表達式使用,如:

char ary[sizeof(int) * 10];

四:各型別的sizeof

1 ) 基本資料型別的sizeof

基本資料型別是指short、int、long、float、double這樣的簡單的內建資料型別,它們都和系統相關,所以在不同的系統下取值可能不同,這務必引起我們的注意,儘量不要在這方面給自己程式的移植造成麻煩

一般的,32位編譯系統中:

sizeof(char); // 1
sizeof(bool); // 1
sizeof(short);// 2
sizeof(int); // 4
sizeof(long); // 4 注意
sizeof(float); // 4
sizeof(double); // 8
sizeof(1.234); // 8 注:與編譯器有關,一般1.234被認為是double型別,所以為8

2 ) 指標變數sizeof

首先我們得了解指標是什麼,指標是程式語言中的一個物件,它的值直接指向存在電腦儲存器中的另一個地方的值。因此將地址形象化的稱為“指標”,它的長度與當前CPU執行模式的定址位數決定,在32位機32位OS的32位編譯器下長度為4,參見:http://blog.sina.com.cn/s/blog_4fd9844201010n3v.html

由上所述,指標變數的sizeof值與指標所指向的物件沒有任何關係,例:

char *pChar = "abcd";
char **pPChar = &pChar;
int *pInt;
string *pStr;
void ( * pf)();

sizeof(pChar); //4
sizeof(pPChar);//4
sizeof(pInt);  //4
sizeof(pStr);  //4
sizeof(pf);    //4

3 ) 陣列的sizeof

陣列的sizeof等於陣列所佔用的記憶體位元組數,陣列的儲存方式為在記憶體中開闢的一塊連續的、大小相同的記憶體空間。

例:

<pre name="code" class="cpp">char a1[4] = "abc";
char a2[] = "abc";
char a3[3];
int a4[3];

sizeof(a1); //4
sizeof(a2); //4
sizeof(a3); //3
sizeof(a4); //3*4 = 12
sizeof(a4[0]); //4

再來看下面的例子:

void f3(char a3[3])
{
int c3 = sizeof(a3);
}
void f4(char a4[])
{
int c4 = sizeof(a4);
}
當陣列a3,a4作為引數傳遞時,會蛻變成指標,相當於char * a3,char *a4,所以c3,c4都為4。

4 ) 結構體的sizeof

4-1) 案例

struct S1
{ char c; int i;};

問sizeof(s1)等於多少聰明的你開始思考了,char佔1個位元組,int佔4個位元組,那麼加起來就應該是5。是這樣嗎?你在你機器上試過了嗎?也許你是對的,但很可能你是錯的!VC6中按預設設定得到的結果為8。我們來好好琢磨一下sizeof的定義——sizeof的結果等於物件或者型別所佔的記憶體位元組數,好吧,那就讓我們來看看S1的記憶體分配情況:

S1 s1={'a',0xFFFFFFFF};
定義上面的變數後,加上斷點,執行程式,觀察s1所在的記憶體,你發現了什麼?以我的VC6.0為例,s1的地址為0x0012FF78,其資料內容如下: 0012FF78: 61 CC CC CC FF FF FF FF 發現了什麼怎麼中間夾雜了3個位元組的CC看看MSDN上的說明:When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment. 4-2) 位元組對齊 原來如此,這就是傳說中的位元組對齊啊!一個重要的話題出現了。 為什麼需要位元組對齊計算機組成原理教導我們這樣有助於加快計算機的取數速度,否則就得多花指令週期了。為此,編譯器預設會對結構體進行處理(實際上其它地方的資料變數也是如此),讓寬度為2的基本資料型別(short等)都位於能被2整除的地址上,讓寬度為4的基本資料型別(int等)都位於能被4整除的地址上,以此類推。這樣,兩個數中間就可能需要加入填充位元組,所以整個結構體的sizeof值就增長了。 讓我們交換一下S1中char與int的位置: structS2{inti;charc;};
看看sizeof(S2)的結果為多少,怎麼還是8再看看記憶體,原來成員c後面仍然有3個填充位元組,為什麼?下面總結規律。 位元組對齊細節和編譯器實現相關,但一般而言,滿足三個準則: (1)結構體變數的首地址能夠被其最寬基本型別成員的大小所整除 (2)結構體每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍,如有需要編譯器會在成員之間加上填充位元組 (3)結構體的總大小為結構體最寬基本型別成員大小的整數倍,如有需要編譯器會在最末一個成員之後加上填充位元組 對於上面的準則,有幾點需要說明: 1) 前面不是說結構體成員的地址是其大小的整數倍,怎麼又說到偏移量了呢?因為有了第1點存在,所以我們就可以只考慮成員的偏移量,這樣思考起來簡單。想想為什麼。 結構體某個成員相對於結構體首地址的偏移量可以通過巨集offsetof()來獲得,這個巨集也在stddef.h中定義,如下:
#defineoffsetof(s,m)(size_t)&(((s*)0)->m)
例如,想要獲得S2中c的偏移量,方法為
size_t pos = offsetof(S2, c); //pos等於4
2) 基本型別是指前面提到的像char、short、int、float、double這樣的內建資料型別,這裡所說的“資料寬度”就是指其sizeof的大小。由於結構體的成員可以是複合型別,比如另外一個結構體,所以在尋找最寬基本型別成員時,應當包括複合型別成員的子成員,而不是把複合成員看成是一個整體。但在確定複合型別成員的偏移位置時則是將複合型別作為整體看待。 這裡敘述起來有點拗口,思考起來也有點撓頭,還是讓我們看看例子吧(具體數值仍以VC6為例,以後不再說明)
struct S3
{
char c1;
S1 s;
char c2;
};
S1的最寬簡單成員的型別為int,S3在考慮最寬簡單型別成員時是將S1“打散”看的,所以S3的最寬簡單型別為int,這樣,通過S3定義的變數,其儲存空間首地址需要被4整除,整個sizeof(S3)的值也應該被4整除。 c1的偏移量為0,s的偏移量呢?這時s是一個整體,它作為結構體變數也滿足前面三個準則,所以其大小為8,偏移量為4,c1與s之間便需要3個填充位元組,而c2與s之間就不需要了,所以c2的偏移量為12,算上c2的大小為13,13是不能被4整除的,這樣末尾還得補上3個填充位元組。最後得到sizeof(S3)的值為16。 通過上面的敘述,我們可以得到一個公式: 結構體的大小 = 最後一個成員的偏移量加上其大小再加上末尾的填充位元組數目,即:
sizeof(struct) = offsetof(lastitem) + sizeof(lastitem) + sizeof(trailingpadding)
4-3)pack指令--調整結構體對齊方式 到這裡,朋友們應該對結構體的sizeof有了一個全新的認識,但不要高興得太早,有一個影響sizeof的重要參量還未被提及,那便是編譯器的pack指令。它是用來調整結構體對齊方式的,不同編譯器名稱和用法略有不同,VC6中通過#pragma pack實現,也可以直接修改/Zp編譯開關。#pragma pack的基本用法為:
#pragma pack(n)
n為位元組對齊數,其取值為1、2、4、8、16,預設是8,如果這個值比結構體成員的sizeof值小,那麼該成員的偏移量應該以此值為準,即是說,結構體成員的偏移量應該取二者的最小值, 公式如下:offsetof(item)=min(n,sizeof(item);再看示例:
#pragma pack(push)//將當前pack設定壓棧儲存
#pragma pack(2)//必須在結構體定義之前使用
struct S1
{char c; int i;};
struct S3
{char c1;S1 s;char c2;};
#pragma pack(pop)//恢復先前的pack設定
計算sizeof(S1)時,min(2, sizeof(i))的值為2,所以i的偏移量為2,加上sizeof(i)等於6,能夠被2整除,所以整個S1的大小為6。同樣,對於sizeof(S3),s的偏移量為2,c2的偏移量為8,加上sizeof(c2)等於9,不能被2整除,新增一個填充位元組,所以sizeof(S3)等於10。 4-4) 空結構體 還有一點要注意,“空結構體”(不含資料成員)的大小不為0,而是1。試想一個“不佔空間”的變數如何被取地址、兩個不同的“空結構體”變數又如何得以區分呢於是,“空結構體”變數也得被儲存,這樣編譯器也就只能為其分配一個位元組的空間用於佔位了。 如下: struct S5 { }; sizeof( S5 ); // 結果為1 4-5) 含位域結構體的sizeof 前面已經說過,位域成員不能單獨被取sizeof值,我們這裡要討論的是含有位域的結構體的sizeof,只是考慮到其特殊性而將其專門列了出來。 C99規定int、unsigned int和bool可以作為位域型別,但編譯器幾乎都對此作了擴充套件,允許其它型別型別的存在。使用位域的主要目的是壓縮儲存,其大致規則為 1) 如果相鄰位域欄位的型別相同,且其位寬之和小於型別的sizeof大小,則後面的欄位將緊鄰前一個欄位儲存,直到不能容納為止; 2) 如果相鄰位域欄位的型別相同,但其位寬之和大於型別的sizeof大小,則後面的欄位將從新的儲存單元開始,其偏移量為其型別大小的整數倍; 3) 如果相鄰的位域欄位的型別不同,則各編譯器的具體實現有差異,VC6採取不壓縮方式,Dev-C++採取壓縮方式; 4) 如果位域欄位之間穿插著非位域欄位,則不進行壓縮 5) 整個結構體的總大小為最寬基本型別成員大小的整數倍。 還是讓我們來看看例子。 示例1:
struct BF1
{
  char f1:3;
  char f2:4;
  char f3:5;
};
其記憶體佈局為:
|_f1__|__f2__|_|____f3___|____|
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|
0 3 7 8 1316 位域型別為char,第1個位元組僅能容納下f1和f2,所以f2被壓縮到第1個位元組中,而f3只能從下一個位元組開始。因此sizeof(BF1)的結果為2。 示例2:
struct BF2
{
  char f1:3;
  shor tf2:4;
  char f3:5;};
由於相鄰位域型別不同,在VC6中其sizeof為6,在Dev-C++中為2。 示例3:
struct BF3
{
  char f1:3;
  char f2;
  char f3:5;};
非位域欄位穿插在其中,不會產生壓縮,在VC6和Dev-C++中得到的大小均為3。

5 ) 聯合的sizeof

結構體在記憶體組織上是順序式的,聯合體則是重疊式,各成員共享一段記憶體,所以整個聯合體的sizeof也就是每個成員sizeof的最大值。結構體的成員也可以是複合型別,這裡,複合型別成員是被作為整體考慮的。 所以,下面例子中,U的sizeof值等於sizeof(s)。
union U
{
  int i;
  char c;
  S1 s;
};

6 ) 列舉的sizeof

在C/C++中,列舉是一個被命名的整型常數的集合。

例:

enum en
{
  e1,
  e2
}_en;

sizeof(_en);得到的值為4,一個整形佔用的位元組數

7 ) 類的sizeof

7-1) 空類

7-2) 類中包含成員

7-3) 類中包含虛擬函式

7-4) 子類

7-5) 子類和父類中都包含有虛擬函式

7-6) 虛繼承的子類

7-7) 既有虛繼承又有虛擬函式的子類

五:與strlen的區別

更新中。。。。