C++ sizeof

轉自:http://krystism.is-programmer.com/

 

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

基本就這些了,如果有紕漏,請指出,謝謝!

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