目錄
sizeof的具體應用場景
- 查看某個類型對象的所佔內存空間的單位字節。
- 在動態分配一對象的時候,可以讓系統知道要分配多少內存。
- 便於一些類型的擴充,在一些平臺上(比如windows、python),創建一個變量,這個變量所存放的內存多少,作爲一個屬性直接放在了這個變量的屬性當中。
- 一些操作數在用的時候,會實時的出現變化,所以,用sizeof可以實時的觀察和記錄佔用內存情況。
- 如果sizeof所進行的對象,是函數或數組的形參,那麼sizeof會直接返回其指針的大小。
常用數據類型大小
- int類型:4bit
- short類型:2bit
- long類型:4bit
- char類型:1bit
- double類型: 8bit
- bool類型:1bit
- float類型:4bit
- 指針類型:4bit
- 字符串類型:"char str[]="hello";",其長度要在最後加1(因爲最後還有個"\n"符號存在佔位),(5+1=6)
總之一句話,大部分都是4bit,最長的也就是8bit(double類型),最小的是1bit(char和bool)。
類對象所佔空間的大小
首先要明白一件事,就是類所創建的對象的大小,並不是簡單的將類中所有的變量進行累加後得來的,而是採用補齊的方式計算得來的!
類的大小必須是其內部成員的最大元素大小的整數倍(本質上,是一個類中的數據存放的時候,第一個數據成員存放在offset爲0的地方,那麼,以後的數據成員,都是存放的起始位置要從上一個成員大小的整數倍後開始存儲)。
#include <stdio.h>
#include <iostream>
class A
{
public:
int i;
};
class B
{
public:
char ch;
private:
};
class C
{
public:
int i;
short j;
private:
};
class D
{
public:
int i;
short j;
char ch;
private:
};
class E
{
public:
int i;
int ii;
short j;
char ch;
char chr;
private:
};
class F
{
public:
int i;
int ii;
int iii;
short j;
char ch;
char chr;
private:
};
int main()
{
printf("sizeof(int) = %d\n", sizeof(int));
printf("sizeof(short) = %d\n", sizeof(short));
printf("sizeof(char) = %d\n", sizeof(char));
printf("sizeof(A) = %d\n", sizeof(A));
printf("sizeof(B) = %d\n", sizeof(B));
printf("sizeof(C) = %d\n", sizeof(C));
printf("sizeof(D) = %d\n", sizeof(D));
printf("sizeof(E) = %d\n", sizeof(E));
printf("sizeof(F) = %d\n", sizeof(F));
system("pause");
return 0;
}
運行如下:
A類包括:1個int->sizeof(A) = sizeof(int) = 4;
B類包括:1個char->sizeof(B) = sizeof(char) = 1;
C類包括:1個int(4字節)+1個short(2字節)->sizeof(C) = 4+2 = 6,因爲6不是4的整數倍,所以,補2,得8;
D類包括:1個int(4字節)+1個short(4字節)+1個char(1字節) ->sizeof(D)= 4+(2+1) =7, 因爲7不是4的整數倍,所以補1,得8;
E類:4+4+2+1+1 = 12,是4的整數倍,得12;
F類:4+4+4+2+1+1 = 16,是4的整數倍,得16;
判斷結果,看加和的結果,是不是其中最大加數的整數倍,如果是,就可以了,如果不是,就補最小的數,使其成爲最大加數的整數倍。
含有虛函數的類對象的空間大小
普通函數不佔用內存,但只要有虛函數,就會佔用一個指針大小的內存(這是虛函數中包含一個虛函數表的緣故),所以,類中有多少個虛函數,就相當於有了幾個指針變量(4字節)。
如下,兩套繼承函數,一套是帶虛函數,一套不帶,它倆的區別就很明顯:
#include <stdio.h>
#include <iostream>
using namespace std;
class Base
{
public:
Base(int x) :a(x)
{
}
void print()
{
printf("base\n");
}
private:
int a;
};
class Derived:public Base
{
public:
Derived(int x) :Base(x - 1), b(x)
{
}
void print()
{
printf("derived\n");
}
private:
int b;
};
class A
{
public:
A(int x) :a(x)
{
}
virtual void print()
{
printf("A");
}
private:
int a;
};
class B:public A
{
public:
B(int x) :A(x - 1), b(x)
{
}
virtual void print()
{
printf("B");
}
private:
int b;
};
int main()
{
Base obj1(1);
printf("sizeof(Base obj1):%d\n", sizeof(obj1));
Derived obj2(2);
printf("sizeof(Base obj2):%d\n", sizeof(obj2));
A a(1);
printf("sizeof(A a):%d\n", sizeof(a));
B b(2);
printf("sizeof(B b):%d\n", sizeof(b));
system("pause");
return 0;
}
需要說明的是,下面的是在x86的環境下編譯,結果是符合預想的:
對於Base,裏面有一個int類型,故Base類所創建的對象大小就是4字節;對於Derived,裏面有個int類型,而且這個類是從Base那繼承過來的,所以在Base的基礎上加4,即8字節;
同理,A的大小就是4+4(int類型+一個虛函數) =8;那麼B就在A的基礎上(這個時候A並沒有調用虛函數,所以不能算上虛函數的那4個字節)也加上了一個Int類型和虛函數:4+4+4=12。
但如果是在x64位下運行:
結果就和之前的不一樣了(具體原因,留待後查)。
注:如果類包含靜態變量,靜態變量所存放的內存空間和類中的局部變量所存在的內存空間並不在一塊兒,所以,當用sizeof去計算包含靜態變量的類的時候,會發現,沒有算靜態變量所佔的大小。
sizeof和strlen的區別
- sizeof是操作符,strlen是定義的函數。
- sizeof可以用類型作參數,比如sizeof(int),但strlen不行,只能用strlen(char* a),而且,strlen是專門測量字符串的,對象必須以”\0“結尾。
- 數組作sizeof的參數的時候,是不會退化成指針的,但作函數的參數的時候,會退化爲一個指針
- sizeof在編譯過程中就把結果(因爲是算類型)計算出來了,但strlen必須是在運行中才可以出結果(因爲是算字符串的長度)。
- sizeof在使用的時候,如果後面是一個類型的,就需要加括號:sizeof(int),但如果是個變量,就不需要加括號了:sizeof a,這是因爲sizeof是個操作符,不是函數。
如果是計算指針指向的字符串的長度,必須用strlen,
字符串用數組來存放
比如:
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
char str[20] = "0123456";
int a = sizeof(str);
printf("a:%d\n", a);
int b = strlen(str);
printf("b:%d\n", b);
system("pause");
return 0;
}
上面就發生了錯亂了,牽扯到計算字符串的長度時候,宜用strlen。
字符串用指針來指向
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
const char* ss = "0123456";
int a = sizeof(ss);
printf("a:%d\n", a);
int b = strlen(ss);
printf("b:%d\n", b);
system("pause");
return 0;
}
運行結果:
用sizeof來計算的話,還是不準,會多出一個“\0”的字符來。
總之一句話,sizeof不能用來計算字符串的長度,只能用strlen來進行。
sizeof(str)/sizeof(str[0])
這個命令是計算str數組中所有元素的個數,並不是計算數組中有效元素的個數,一定要區分清楚!
聯合體大小的計算
聯合體的大小取決於:
- 其大小爲所有的成員中佔用空間最大的一個成員的大小(union獨有的)
- 對齊方式爲成員中最大的成員的數據類型長度的整數倍(和union、struct、class在對齊方式上是一樣的)
看下面的代碼,
#include <stdio.h>
#include <iostream>
using namespace std;
union u0
{
int i;
double j;
};
union u1
{
char a[13];
int i;
};
union u2
{
char a[13];
char b;
};
int main()
{
printf("u0:%d\n", sizeof(u0));
printf("u1:%d\n", sizeof(u1));
printf("u2:%d\n", sizeof(u2));
system("pause");
return 0;
}
先看u0,裏面一個double(8),一個int(4),union的大小取決於裏面所存放的最大類型,很顯然,最大類型的double,那就8,再看對齊方式有沒有問題,沒問題,8是8的整數倍。
再看u1,裏面有個char數組,是一個變量,所以,看作一個類型(大小爲13),和int(4字節),那麼找所存放的最大類型爲13 ,再看,對齊原則,char的大小是(1字節),int的大小是4,那這個聯合體的大小應該是4的整數倍,所以,(13+3)=16,湊夠4的整數倍。(注意數組的特殊性,大小和最小單位,換句話說,看對齊方式的時候,char的數組的最小單位是1字節)
同理,u2,裏面也是13,但最大字節數變成了char(1字節了),13是1的整數倍,所以是13
運行結果如下:
#pragma pack(x)強制修改編譯器對齊方式
把上面的一個C類拿出來,按照正常的操作,C的大小應該是8,因爲要對齊(C類包括:1個int(4字節)+1個short(2字節)->sizeof(C) = 4+2 = 6,因爲6不是4的整數倍,所以,補2,得8;)。但我用 #pragma pack(1),強制將對齊的最小單位設置成了1,這樣,類的大小,就變成了直接把類中的變量類型進行累計就行了!
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma pack(1)
class C
{
public:
int i;
short j;
private:
};
int main()
{
printf("c:%d\n", sizeof(C)); //原來因爲要對齊int類型,結果爲8
system("pause");
return 0;
}