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.当前编译所采用的对齐方式