void类型的指针
1. void就是空的意思, void类型的指针。
2. void*point; 这样就定义了一个void类型的指针。
3. 不能只用*来直接访问void类型的指针, *point; // 会报错
不能对指针进行++,point++;// 会报错, 是因为void类型指针,对指针进行++,不知道要增加多少个
字节。
4. void类型指针可以隐式转换为任何类型的指针。
int nub = 10; int *a = &nub; void* b = a; // a为int类型的指针在赋值给void类型的指针b时自动转换成了void类型。
5. void类型在使用的时候,必须强制转换成相应的类型才可以使用。
int c = *((int*) b); // b为void类型指针,强制转换为int类型的指针,这样就可以访问和运算了。
那为什么要使用void类型的指针呢? 看后面。
函数指针
函数指针: 顾名思义就是指向函数的指针。
所以,函数也是有地址的,函数的名称在编译之后就转化为函数的入口地址了,系统通过这个地址找到函数的位置,然后按顺序执行函数中的指令。
int compare_int(const void* a, const void* b)
{
//printf("成功调用函数");
int *a1 = (int*)a;
int *b1 = (int*)b;
return *a1 - *b1; // *b1 - *a1 反过来了,从大到小排列
}
int main(void)
{
int a = 0;
int b = 10;
/*既然函数也有地址,那我们可以定义一个指针指向函数*/
int (*fun_point)(const void*, const void*); // 定义了一个可以指向函数的指针
fun_point = &compare_int;
// 第一种
(*fun_point)(&a, &b); // 含义: *fun_point去访问这个函数,但是函数调用需要传参数,所以应该传
相应的参数
// 第二种
compare_int(&a, &b); // 不用*可以直接调用。
system("pause");
return 0;
}
上面的代码定义了一个函数指针, 和数组指针的原理是类似的。
int (*fun_point)(const void*, const void*); 定义了一个指针,指向参数为(const void*, const void*)返回值为 int 的函数。
而我们可以创建这样的一个函数, compare_int; // 可以使用这个指针指向这个函数
fun_point = compare_int;
注意: 使用函数指针指向的函数,必须和函数指针指向的函数类型(返回类型和参数)一致才行,(也就是定义函数指针时,其指向的函数类型),否则是不对的。
指针已经指向了函数,我们可以使用它访问这个函数, 因为访问的是函数,所以我们应该传入相应的参数(代码中有两种访问方式,主要是历史上有的人使用第一种,有的人使用第二种,所以两种都是可以的)。
(*fun_point)(&a, &b); 对于这段代码,其实和compare_int(&a,&b)是一致的。因为compare_int就是函数的入口地址,执行函数调用,就是跳转到函数的位置,然后将实参赋值给函数形参,然后执行函数中的指令。
那么为什么要使用函数指针呢?
使用函数指针和void类型的指针
我们以c库函数中的一个快速排序的函数,qsort().
这个函数接收4个数据: 1. 需要排序数的地址
2. 传入数据的占用的内存
3. 传入数据类型的大小
4. 传入一个函数,指明排序规则
上面的第一个参数: 它使用的就是一个void*类型的指针, 因为任何指针都可以隐式转换成void类型,所以这可以使qsort()接收任何类型的数据,也就可以实现任何类型值得排序。(void类型指针在此处得作用)。如果qsort接收得参数是int 类型得指针,那么就只能传入int得值,只能实现int类型指针得排序 。
第四个参数: 很明显是需要传入一个函数得,那怎么将函数作为参数传入呢,我们可以定义一个函数指针指向对应函数,然后将这个函数指针传入qsort,这样它就可以使用传入得函数指针调用函数(这个中指明了排序的规则和排序的类型)。 (函数指针在此处得使用)
#include
#include
int compare_int(const void* a, const void* b)
{
//printf("成功调用函数");
int *a1 = (int*)a; // 函数指明排序的类型为int 则将传入的void*转为int*
int *b1 = (int*)b;
// 从大到小排
return *a1 - *b1; // *b1 - *a1 反过来了,从大到小排列
}
int compare_char(const void* a, const void* b)
{
char a1 = *(char*)a; // 函数指明排序的类型为char,则将传入的void*转换为char*
char b1 = *(char*)b;
//不区分大小写,那就先将其都转换成小写,换成大写也一样
if (a1 >= 'A' && a1 <= 'Z')
{
a1 = a1 + 32; // 转化成小写
}
if (b1 >= 'A' && b1 <= 'Z')
{
b1 = b1 + 32; // 转化成小写
}
// 从大到小排 b1-a1是从大到小排
return a1 - b1;
}
int main(void)
{
int arr[] = { 2,10,1,11,8,7,156,520 };
/*对整型数组进行排序*/
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), &compare_int);
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d\n", arr[i]);
}
/*对任何类型进行排序*/
char arr1[] = "abcdefghiABCDEFGHI"; // 按照字母排序不区分大小写
// 减1是因为,字符0不参与排序
qsort(arr1, sizeof(arr1) / sizeof(char)-1, sizeof(char), &compare_char);
for (int i = 0; i < sizeof(arr1) / sizeof(arr1[0]); i++)
{
printf("%c\n", arr1[i]);
}
system("pause");
return 0;
}
其实我们在使用指针是,常常还会使用到一个动态分配内存的函数 malloc(); 我们在使用的时候都需要进行类型强转,这其实就是,malloc返回一个void类型的指针,我们需要什么样的类型就强转成什么。这就体现了void*指针的优点, 可以让我们申请各种类型的内存,而不是设置成只能申请一种,只要我们将返回的指针强转成一个我们需要的类型就可以了。
上面的代码,实现使用qsort分别对int类型数组和char类型数组进行了排序。
void类型指针作为参数,可以使任何类型的数据传入函数。
函数指针可以让函数作为参数传入另外一个函数。