void的字面值是「無類型」,void*則是"無類型指針"。void*能夠指向任何類型的數據。void幾乎只有"註釋"和限制程序的做用,由於歷來沒有人會定義一個void變量。程序員
void a; //編譯時提示"illegaluseoftype'void'"express
void真正發揮的做用在於:對函數返回的限定;對函數參數的限定編程
若是指針p1和p2的類型相同,那麼p1和p2之間可互相賦值;若是p1和p2指向不一樣的數據類型,則必須使用強制類型轉換運算符,把賦值運算符右邊的指針類型轉換爲左邊指針的類型,而後才能夠賦值。如:數組
float* p1;數據結構
int* p2;函數
p1 = p2; //can't convert from 'int*' to 'float*',必須改成下面這種形式spa
p1=(float*)p2;指針
而void*則不一樣,任何類型的指針均可以直接賦值給它,無須強制類型轉換,例如:void* p1;int* p2;p1 = p2;對象
但這並不意味灃void*也可無須強制轉換地賦值給其餘類型的指針,這是由於"無類型"能夠包含"有類型",而"有類型"不能包容"無類型"繼承
void關鍵字使用規則
1.若是函數沒有返回值,那麼應聲明爲void類型。在C語言中,凡不加返回值類型限定的函數,就會編譯器做爲返回整型值處理,可是許多程序員卻誤認爲其爲void類型
例如
add(int a,int b) int main()
{ {
return a+b; printf("2+3=%d",add(2,3));
} } //程序運行結果爲2+3=5,這說明不加返回值說明的函數,其返回值的確爲int類型
所以,爲避免混亂,對於任何函數都必須指定其返回類型。若是函數沒有返回值,必定要聲明爲void類型。發揮代碼的自注釋做用。這既是程序良好可讀性的須要,也是編程規範性的要求
2.若是函數無參數,那麼應聲明其參數爲void.若是在C++語言中聲明一個這樣的函數
int function(void) //則進行function(2)是不合法的,由於在C++中,函數參數爲void的意思是這個函數不接受任何參數,但若是在Turbo C2.0中編譯則正確
{ //說明在C語言中,能夠給無參數的函數傳送任意類型的參數,但在C++編譯器中編譯一樣的代碼則會出錯。因此,若函數不接受任何參數,必定要聲明爲void
return 1;
}
3.若是函數的參數能夠是任意類型的指針,那麼應聲明其參數爲void*
典型的如內存操做函數:
void* memcpy(void* dest,const void* src,size_t len);
void* memset(void* buffer,int c,size_t num);
這樣,任何類型的指針均可以傳入memcpy和memset中,這也真實地體現了內存操做函數的意義,由於它操做的對象僅僅是一片內存,而不論這片內存是什麼類型
4.void不能表明一個真實的變量
void a //錯誤
void體現了一種抽象,這個世界上的變量都是"有類型"的。void的出現只是爲了一種抽象的須要,若是你正確地理解了面向對象中"抽象基類"的概念,也很容易理解void數據類型。
二.sizeof
sizeof不是一個函數,由於sizeof unary-expression,sizeof(unary-expression)和sizeof(type-name)都合法,而第一種沒有括號
sizeof不是一元操做符,int a = 0;cout<<sizeof(a=3)<<endl;cout<<a<<endl; 運行結果爲4,0而不是4,3,因此不是一元操做符
這是因爲sizeof在編譯階段處理的特性形成的。sizeof不能被編譯成機器碼,sizeof做用範圍內,也就是()裏面的內容也不能被編譯(a=3語句不能編譯成機器碼),而是被替換成類型,因此,
sizeof不是一元操做符,由於sizeof不支持鏈式表達式,sizeof做用域範圍內的語句不會編譯成機器碼。更像一個特殊的宏,它在編譯階段求值,例如
cout<<sizeof(int)<<endl; //32位機上輸出4
cout<<sizeof(1==2)<<endl; //至關於size(bool),爲1
1.sizeof的用法
sizeof有兩種用法,第一種爲針對對象,用法爲sizeof(object)或sizeof object,第二種爲針對類型,用法爲sizeof(typename),例如
int i = 2;
cout<<sizeof(i)<<endl; //sizeof(object)的用法,合理
cout<<sizeof i<<endl; //sizeof object的用法,合理
cout<<sizeof 2<<endl; //2被解析成int類型的object,sizeof object的用法,合理
cout<<sizeof(int)<<endl; //sizeof(typename)的用法,合理
cout<<sizeof int<<endl; //錯誤,對於操做符,必定要加(),不管是對對象仍是對類型取值,sizeof寫成sizeof這種函數形式永遠是正確的
2.基本數據類型的sizeof
函數類型和指針類型都屬於指針的範疇,指針主要用於存儲地址,在32位系統下,任何類型的指針其所佔用的內存大小均爲4字節;而函數類型比較特別,它以其返回類型做爲自身類型進行sizeof取值。不管指針是什麼類型,只要是指針,sizeof運算結果都是4
函數類型以其返回值的類型做爲自身類型。注意不能對返回void的函數進行sizeof取值,也不能對函數指針進行sizeof取值
int f1() double f2() void f2()
{ { {
return 0; return 0; return 0;
} } }
cout<<sizeof(f1())<<endl; //f1()返回值爲int,所以被認爲是int
cout<<sizeof(f2())<<endl; //f2()返回值爲double,所以被認爲是double
cout<<sizeof(f3())<<endl; //錯誤,沒法對void類型使用sizeof
cout<<sizeof(f1)<<endl; //錯誤,沒法對函數指針使用sizeof
下面是數組的sizeof
char a[] = "abvdef";
char b[] = {'a','b','c','d','e','f'};
int c[20] = {3,4}
char d[2][3]={"aa","bb"};
cout<<sizeof(a)<<endl; //輸出7,表示字符串
cout<<sizeof(b)<<endl; //輸出6,僅表示字符數組
cout<<sizeof(c)<<endl; //輸出80
cout<<sizeof(d)<<endl; //輸出6
cout<<sizeof(*a)<<endl; //輸出1
cout<<sizeof(*b)<<endl; //輸出1
cout<<sizeof(*c)<<endl; //輸出4
cout<<sizeof(*d)<<endl; //輸出3
若是字符數組表示字符串的話,數組末尾自動插入的'\0',在sizeof時不能遺漏,因此數組a的大小在定義時未指定,編譯時給它分配的空間是按照初始化的值肯定的,也就是7。c是多維數組,佔用的空間大小是各維數的乘積,也就是6,能夠看出,數組的大小就是它在編譯時被分配的空間,也就是各維數的乘積,也就是6。能夠看出數組的大小就是它在編譯時被分配的空間,也就是各維數的乘積*數組元素的大小。對應二維數組d,*d等價於d[0](d[0]是一維數組長度爲3),因此sizeof(*d)等於3
C語言中多維數組的實現是經過一維數組實現的,也就是說對於多維數組,你所操做的數組元素有可能自己就是一個數組。這是應該引發注意的。數組的大小是各維數的乘積*數組元素的大小
向函數開通傳遞數組,數組將會退化爲指針,失去了原有數組的特性
int GetStrLength(char str[]) int main()
{ {
return sizeof(str); char szStr[] = "abcdef";
} cout<<"sizeof(szStr[])="<<GetStrLength()<<endl;
} //輸出sizeof(szStr[])=4
struct的sizeof爲CPU對齊問題
對一個空struct結構體取sizeof運算,運算結果爲1而不是0。這是編譯器爲了保證此空struct存在而專門分配的這一字節,若是存在結構體嵌套,不管外層仍是內層均需採用內存對齊
class數據結構的sizeof問題
1.不含繼承和static成員變量的類
此種狀況下,類類型和struct類型對象大小的處理方式相同,只需考慮對齊方式便可,例如
class CA
{
};
class CB
{
private:
int m_nValue;
double m_dValue;
public:
int GetInt() {return m_nValue;}
double GetDouble(){return m_dValue;}
};
cout<<"sizeof(CA)="<<sizeof(CA)<<endl;
cout<<"sizeof(CB)="<<sizeof(CB)<<endl;
運行結果以下:
sizeof(CA) = 1 sizeof(CB) = 16
總結:
不含繼承和static成員變量的類,類對象大小計算遵循下面的準則
和struct複合類型同樣,空的class一樣也佔用1字節
class類型和struct同樣採用對齊來計算對象的大小
計算類對象的大小時,類的成員函數不佔用對象空間,只需考慮類中數據成員的大小便可。由於它們爲全部的類對象共享,存儲於代碼段中
2.包含繼承和static成員變量的類
在單繼承狀況下,只要class中存在virtual函數,在編譯時編譯器就會自動插入一個指向虛函數表的指針vptr(大小爲4字節)。不一樣的編譯器,vptr插入的位置可能不一樣,VC編譯器插入vptr的位置通常從數據成員開始。而static成員是分配在全局區爲類的全部對象共享(VC編譯器可能爲了方便將其放入文字常量表),sizeof時不該計入static成員。
class CBase
{
public:
virtual void foo(){};
private:
int m_nValue1;
}
class CStaticClass
{
private:
static double m_dValue;
};
class CDrivedA:public CBase
{
private:
virtual void foo(){}
private:
int m_nValue2;
};
int main()
{
cout<<"sizeof(CStaticClass)="<<sizeof(CStaticClass)<<endl;
cout<<"sizeof(CBase)="<<sizeof(CStaticClass)<<endl;
cout<<"sizeof(CDrived)="<<sizeof(CStaticClass)<<endl;
return 0;
}
執行結果爲:
sizeof(CStaticClass)=1
sizeof(CBase)=8
sizeof(CDrived)=12
對於類CStaticClass,雖然包含了一個static double類型的成員變量,可是CStaticClass大小倒是1,因此能夠看出static成員變量不佔用對象大小。對於CBase類只包含一個成員變量,理論大小應該是4,而實際是8,因此class中存在virtual函數,編譯器就會自動插入一個指向虛函數表的指針vptr(大小爲4字節)
影響類大小的因素總結以下:
1.類的非static類型數據成員
2.類是否存在virtual成員函數
3.當前編譯所採用的對齊方式