C++進階—>各類型佔大小及sizeof函數

本文針對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,無論是在成員函數,還是在繼承上,都額外加一個指針大小空間。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章