本文針對32位編譯器而言,64位編譯器同理。
/****************************各類型所佔大小*******************************/
本部分參考http://blog.csdn.net/ynnmnm/article/details/45826963,但存在改進!
#include <iostream>
using namespace std;
////////// 測試基礎類型 //////////
int GetArraySize(char str[])
{
return sizeof(str);
}
void TestSizeofBase()
{
cout << "////////// 測試基礎類型 //////////" << endl;
char* pStr = "hello world";
char arrStr[] = "hello world";
cout << "char: " << sizeof(char) << endl;
cout << "short: " << sizeof(short) << endl;
cout << "int: " << sizeof(int) << endl;
cout << "float: " << sizeof(float) << endl;
cout << "double: " << sizeof(double) << endl;
cout << "char*: " << sizeof(pStr) << endl;
cout << "string: " << sizeof("hello world") << endl;
cout << "char[]: " << sizeof(arrStr) << endl;
cout << "char[] as param: " << sizeof(GetArraySize(arrStr)) << endl;
cout << endl;
}
//////////// 測試空類 ////////////
class EmptyA
{
};
class EmptyB : public EmptyA
{
};
struct EmptyStruct
{
};
void TestSizeofEmpty()
{
cout << "//////////// 測試空類 ////////////" << endl;
cout << "EmptyA: " << sizeof(EmptyA) << endl;
cout << "EmptyB: " << sizeof(EmptyB) << endl;
cout << "EmptyStruct: " << sizeof(EmptyStruct) << endl;
cout << endl;
}
////////// 測試內存對齊 //////////
class AlignA
{
char a;
};
class AlignB
{
char a;
short b;
};
class AlignC
{
char a;
short b;
double c;
};
#pragma pack(1)
class AlignD
{
char a;
short b;
double c;
};
#pragma pack()
void TestSizeofAlign()
{
cout << "////////// 測試內存對齊 //////////" << endl;
cout << "AlignA: " << sizeof(AlignA) << endl;
cout << "AlignB: " << sizeof(AlignB) << endl;
cout << "AlignC: " << sizeof(AlignC) << endl;
cout << "AlignD: " << sizeof(AlignD) << endl;
cout << endl;
}
//////////// 測試多態 ////////////
class VirtualA
{
virtual void test();
};
class VirtualB
{
virtual void test();
virtual void test2();
};
void VirtualB::test() {}
void VirtualB::test2() {}
class VirtualC
{
char a;
virtual void test();
virtual void test2();
};
void TestSizeofVirtual()
{
cout << "//////////// 測試多態 ////////////" << endl;
VirtualB objB;
cout << "VirtualA: " << sizeof(VirtualA) << endl;
cout << "VirtualB: " << sizeof(VirtualB) << endl;
cout << "VirtualB Object: " << sizeof(objB) << endl;
cout << "VirtualC: " << sizeof(VirtualC) << endl;
cout << endl;
}
void TestSizeof()
{
TestSizeofBase();
TestSizeofEmpty();
TestSizeofAlign();
TestSizeofVirtual();
}
int main(int argc, char **argv)
{
TestSizeof();
getchar();
return 0;
}
輸出結果
簡要說明
char數組在計算大小的時候分配了大小算總值,若賦了初始值除了字符數之和還要算上結束符\0.
指針類型都是佔4字節,因爲在32位編譯器來說都是地址都是32位的無符號int型,故4字節。
sizeof(GetArraySize(arrStr)) 4,是因爲參數傳遞爲形參時,把數組轉爲了指針。
sizeof(空類) 1,是因爲實例化類需要編譯器給它分配內存空間,不能分配爲size爲0的內存,所以編譯器默認分配了一個字節,以便標記可能初始化的類實例,同時使空類佔用的空間也最少(即1字節)。
內存對齊,對象的首地址能夠被其最寬基本類型成員的大小所整除;每個成員相對於結構體首地址的偏移量(offset)都是成員大小的整數倍, 如有需要編譯器會在成員之間加上填充字節(internal adding);總大小爲最寬基本類型成員大小的整數倍。
虛函數,虛函數表指針,大小相當於sizeof(void *)。
/***************************sizeof函數****************************/
本部分參考http://www.cnblogs.com/zhangyz/articles/4736758.html ,但存在改進!
學過c的都知道sizeof運算符。不過還是需要注意以下幾點。先從c的sizeof說起:
1. sizeof 是運算符,而不是函數。雖然我們習慣sizeof(...),但( )並不是必需的,它只是表示優先級。我們把sizeof後面的目標叫對象或者操作數。本文約定就叫sizeof對象。
2. 當sizeof 的對象是表達式時,求的大小是表達式返回值的類型大小,但並不計算表達式的值,比如
1
2
3
4
|
char c
= 1; int i
= 2; cout
<< sizeof (c
+ i) << endl; cout
<< sizeof (c
= c + i) << endl; |
前者c + i會隱式類型轉化爲int類型(類型提升),因此返回4(32位系統), 而後者雖然運算時也是轉化爲int,但賦值給c時又會轉化爲char,因此返回的是1。同樣如果對象是函數,則返回函數返回值類型大小,如:
1
2
3
4
5
6
7
8
9
10
11
|
long long foo() { printf ( "'%s'
has been called.\n" ,
__func__); return 0; } int main( int argc, char **argv) { cout
<< sizeof (foo())
<< endl; return 0; } |
執行後輸出8, 不會輸出 'foo' has been called.說明函數沒有真正執行,而只是判斷了下返回類型。
3.注意sizeof 對象是指針和數組的區別。
當sizeof的對象是數組時,返回數組總大小,而當對象是指針時,返回指針本身的大小,而不是指示內存空間的大小。因爲指針本身就是一個無符號整型數, 因此int *p ,sizeof(p)返回的大小是sizeof(void *), 32 位系統返回4,即32位。但注意當數組名作爲實參傳入函數時,會自動轉化爲指針類型,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void foo( int a[]) { cout
<< sizeof (a)
<< endl; /*
4 */ } int main( int argc, char **argv) { int a[]
= {1, 2, 3, 4}; int *p
= a; cout
<< sizeof (a)
<< endl; /*
16 */ cout
<< sizeof (p)
<< endl; /*
4 */ foo(a); return 0; } |
4. sizeof 無法獲取動態分配的內存大小,即使用malloc動態的分配內存,無法使用sizeof獲取其大小。
5. 注意c_style字符串末尾有一個\0結束符,也需要佔一個char空間,因此sizeof("1") 返回2。而strlen返回的是字符數,不包括\0結束符。
6.關於結構體類型。
理論上一個結構體所佔空間是所有成員的大小總和,但由於考慮到對齊問題,會有填充字節。
1
2
3
4
5
|
struct node { int a; char c; }; |
大小爲8字節而不是5字節,填充了3字節。
注意:c語言中空struct大小爲0, 而c++中空struct 大小爲1, 具體看後面關於空類的討論。另外,c99中結構體後面的動態數組,即不指定大小的數組,sizeof 時不包括動態數組的大小,即
1
2
3
4
5
6
|
struct node { int a; char c; int d[]; }; |
返回依然是8。
下面關於c++類的討論。除了struct ,以上討論關於c的sizeof同樣適合於c++。首先說說c++ 中的struct類型,注意和c中的struct是不一樣的,c中的struct只是一種把各種基本數據類型包裝的組合類型,而c++的struct本質 上是類,即類有的東西,struct基本都有,即struct也有構造函數、析構函數、成員函數等等,不過它的默認成員是public的,而class定 義的類成員默認是private的。另外,struct繼承默認也是public,而class定義的類默認是private。另外注意:class可以定義模板參數,但struct不可以!因此,struct本質就是類。
下面主要討論類的大小:
1. 空類的大小。空類型實例中不包含任何信息,應該大小爲0. 但是當我們聲明該類型的實例的時候,它必須在內存中佔有一定的空間,否則無法使用這些實例。至於佔用多少內存,由編譯器決定。g++中每個空類型的實例佔1字節空間。注意空struct即空類,這就是爲什麼c++的空struct佔一個字節的原因。
2. 構造函數、析構函數、成員函數調用時只需知道函數地址即可,而這些函數的地址之與類型相關,而與具體的實例無關,因此不會在實例中額外添加任何信息。
3. 靜態數據成員放在全局數據成員中,它不佔類實例大小,多個類實例只有一個實體。可以看作是一種特殊的全局變量。
綜上1,2,3:
1
2
3
4
5
6
7
8
9
|
class A { public : static int a; static char c; A(){}; ~A(){}; void foo(){}; }; |
類A的大小爲1字節,等於空類大小,因此靜態數據成員a,c和成員函數都不佔類的大小。
4. 類的非靜態數據成員和c語言中的struct類似,也需要對齊,可能需要字節填充。
1
2
3
4
5
6
7
8
9
|
class A { public : int a; char c; A(){}; ~A(){}; void foo(){}; }; |
類A的大小爲8字節,a佔4B,c佔1B,填充3B。
5. 如果一個類中有虛函數,則該類型會生成一個虛函數表,並在該類型的每一個實例中添加一個指向虛函數表的指針,因此類大小必須加上一個指針所佔的空間。如果是普通繼承,子類和基類共享這個指針。
1
2
3
4
5
6
7
8
9
10
|
class A { public : int a; char c; A(){}; ~A(){}; void foo(){}; void virtual bar(){}; }; |
類A的大小爲12B。數據成員8B,加上指向虛擬函數表的指針。注意,是在32位系統上。如果是64位機器,一個指針佔8B。
6.虛繼承時,派生類會生成一個指向虛基類表的指針,佔一個指針大小空間。如果還有虛函數,不增加額外指針大小空間,原因不太清楚,如果誰知道,請一定要告訴我!如下:
1
2
3
4
5
6
7
8
9
|
class A { int a; }; class B: public virtual A { int b; virtual void foo(){}; }; |
類B的大小爲12B,數據成員b佔4B,從A中繼承a也佔4B,另外一個由於virtual存在,額外加一個指針大小4B,共12B。所以:只要有virtual,無論是在成員函數,還是在繼承上,都額外加一個指針大小空間。