在讲void指针之前,我们先回忆一下指针:
int *p;
char *q;
这里定义了两个指针变量p和q,其中,p指向返回值类型为int的变量,q指向返回值为char的变量。
而void是一种无类型指针,它表示不知道指向什么类型的指针,所以void指针可以指向任意类型的数据。做运算时,void相当于char(只针对部分编译器而言),但是和char*并不相通。*
如下,当普通指针进行强制类型转换时可能会出现错误:
#include <stdio.h>
int main() {
char i1[4] = {'a', 'b', 'c', 'd'};
char *p1 = &i1;
for (int i = 0; i < 4; i++)
printf("%c\n", i1[i]);
//让指向char数组的char指针为0
*p1 = 0;
for (int i = 0; i < 4; i++)
printf("%d\n", i1[i]);//ASCII值为0的时候对应的字符是NULL,控制台什么都不会显示(或者说显示的是空字符),所以这里我们换成十进制输出
//让指向char数组的int指针为0
int *p2 = (int *)p1;
*p2 = 0;
for (int i = 0; i < 4; i++)
printf("%d\n", i1[i]);
return 0;
}
当我们把一个char指针强制转换成int指针后(不建议这样做,编译时会出现warning),它从指向一个字节变成了指向连续的四个字节,此时我们对这个int指针赋值,会导致连续的四个字节的变量都会被覆盖,从而导致值的丢失。
任何类型的指针都可以直接赋值给 void 指针,而它会无条件接受,且无需进行其他相关的强制类型转换,所以可以使用void指针作为函数参数,这样就不会造成精度丢失或者值意外被覆盖。不过要将 void 指针赋值给其他类型的指针,必须进行强制类型转换。
讲完了void*我们就可以更好地理解malloc函数。
如果我们需要一个变长的数组,可以定义一个int型的长度length,然后通过scanf确定Array[length]的长度。
但是,C99之前,数组的下标不能是变量,所以当我们提前不知道数组长度时,需要用malloc函数动态地分配内存,通过运行时输入的数值决定申请多少字节的内存。
使用malloc这个函数需要包含stdlib.h头文件,其函数原型为
void*malloc(size_t size);
其中size是申请的空间大小,单位为字节。
我们可以用malloc()向系统申请空间,申请到的空间是void无类型的,在计算机看来,内存就是连续的空间,它并不在意你申请的是什么类型,更在意的是你申请了多少字节。用malloc()申请空间之后我们可以自由决定它的类型。
比如像这样:
#include<stdio.h>
#include<stdlib.h>
int main(){
int length;
int*arr;
printf("输入长度:\n");
scanf("%d",&length);
arr=(int*)malloc(length*sizeof(int));
printf("输入数组的值:\n");
for(int i=0;i<length;i++){
scanf("%d",&arr[i]);
}
free(arr);
return 0;
}
运行结果如下:
数组是const类型的特殊指针,所以我们可以对指针类似数组使用,用[ ]操作。
用malloc()申请空间后还要用free()释放,malloc()和free()成对出现。
free函数原型如下:
void*free(void *ptr);
注意这里free的指针变量必须是当初申请的首地址,如果我们对malloc()的指针变量p进行了运算,例如自增自减(p++、p–),仍然free的是p,这时候p已经不是当时申请的首地址了,计算机无法正确地回收内存空间。