void类型

void的字面意思是“无类型”,void * 则为无类型指针,void *可以指向任何类型的数据。
void几乎只有“注释”和限制程序的作用。
void a;这行语句编译时会出错,提示”illegal use of type ‘void’”。不过,即使void a的编译不会出错,它也没有任何实际意义。

void真正发挥作用在于:

1.对函数返回的限定:

如果函数没有返回值,那么应声明为void类型。
在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理:不加返回值说明的函数返回int型。不能寄希望于编译器会做严格的类型检查,为了避免混乱,我们在编写C/C 程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类
型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。

2.对函数参数的限定:

如果函数无参数,那么应声明其参数为void。
在C中,函数参数为void的意思是这个函数不接受任何参数。若函数不接受任何参数,一定要指明参数为void。

3.void型指针:

因为void *可以指向任何类型的数据,所以void指针一般被称为通用指针或者泛指针,或者叫做万能指针。
定义形式:void *p;
在C语言中在任何时候都可以用void类型的指针来代替其他类型的指针,void指针可以指向任何数据类型的变量。

void指针

使用void类型指针要注意的几条规则:

void*类型只有地址信息没有类型信息,所以不能计算偏移。

C/C++中的指针通常来说有两个属性:
1.指向变量
2.指向对象的地址和长度
指针其实就是存储被指向变量的地址,并不保存其长度;
而且存的这个地址仅是变量的首地址,并不是该变量占据内存的所有地址空间。如:

1
2
int a=3;
int *p=&a;

目前大多数的C/C++编译环境中,整型int数据占4个字节的空间,所以指针p存储的地址(即指向的a的地址)为4个字节空间的首地址。
当需要读取一个例如int型数据时,编译器根据指针的类型从指针指向的地址开始向后寻址。指针类型不同则寻址范围也不同,比如:
int*从指定地址向后寻找4字节作为变量的存储单元;
double*从指定地址向后寻找8字节作为变量的存储单元。

说到这里可能大家就会有一个问题:由于计算机内部的地址是整型数字,那么为什么不干脆用一个整型变量存储地址,还要发明指针变量呢?
如果我们从指针实现的角度讲,指针就是一个整型变量,它存储的是一个地址值,没有任何附加信息。目前为止貌似没有什么问题,其实不然。
就拿上述的代码:如果用一个整型变量b存储a的地址,即int b=&a。当对b加1时,得到的新的地址相当于对a的首地址加1,即&a+1,由于int占连续的四个存储单元(默认),此时b存储的是第二块存储单元的地址,所以根据变量b存储的地址,将无法完整的读出变量a的值,导致错误。而通过指针变量,可以解决这类问题:如果对上述代码中的指针p加1的话,实际上是p+sizeof(int),一次性增加了4个存储单元。而void*是一种特别的指针,因为它没有指向的类型,或者说不能根据这个类型判断出指向对象的长度。

任何指针(包括函数指针)都可以赋值给void指针。

例:

1
2
3
void *vp;
type *p;
vp=p;

特点:
1.不需要类型转换
2.只获得变量/对象地址而不获得大小。

void指针赋值给其他类型的指针时都要进行转换。

例:

1
2
3
void *vp;
type *p;
p=(type *)vp;

特点:
1.转换类型也就是获得指向变量/对象大小

void指针在强制转换成具体类型前,不能解引用。

例:

1
2
void *vp;
*vp; //这是错误的用法

原因:
void指针只知道指向变量/对象的起始地址,而不知道指向变量/对象的大小(占几个字节)所以无法正确引用。

void指针不能参与指针运算,除非进行转换。

例:

1
2
3
void *vp;
type *p;
(type *)vp++;

说明:
(type *)vp++ 等价于 vp=vp+sizeof(type)

void指针的应用场合

void指针有以下几种应用场合:

当进行纯粹的内存操作的时候,可以使用void指针。

典型的如内存操作函数memcpy和memset的函数原型分别为:

1
2
void * memcpy(void *dest, const void *src, size_t len); 
void * memset ( void * buffer, int c, size_t num );

这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论这片内存是什么类型。

当传递一个指向未定类型的指针时候,可以使用void指针。

可以作为函数模板,链表等参数的通用参数。在使用时,只需要强制类型转换就可以。

指向0的地址。

(void *)0,指向全是0的地址,相当于NULL。

数据指针。

数据指针的概念:
在嵌入式编程当中,可能在特定的内存但愿读写内容,汇编有对应的MOV命令,而除了C/C++以外的编程语言基本上没有直接访问内存的能力。利用数据指针可直接通过内存地址操作特定的内存。
例:在地址0XFF00FF00对应的内存单元中写入11:

1
2
unsigned int *p = (unsigned int *)0XFF00FF00;
*p = 11;

数据指针操作特定的内存的时候一定要谨慎,不是所有内存都可以操作,必须要对硬件比较了解后,才能进行操作(可能会出现段错误)。

以上内容整理自:
void类型及void指针
C语言指针高级部分:void指针和数据指针
无类型指针void*的学习与使用