前言
上篇文章,我们学习了字符指针、指针数组、数组指针、函数指针,接下来我们来一起探讨更深层次的指针进阶的内容!!!
一、函数指针数组
1.1认识函数指针数组
前面我们学到了函数指针,函数指针是存放函数地址的指针,其表现形式为:
int*(*pa)(int,int)= &add 表示将add函数的地址传递给pa这个函数指针,这个函数的形参为两个int类型,返回参数为int类型的数据,这就是函数指针。
应用:
1.int len =(*pa)(2,1) 表示的是使用add函数传参为2,1 然后返回的数值给 len
2.int len = pa(2,1) 因为pa其实就是得到函数的地址,正常函数的使用的时候,也是函数名加传参,函数名的地址和取地址函数名的地址是一样的,所以是可以不需要解引用(*pa),这个星号是无所谓的,即使是******pa也不会影响结果
接下来,是对于函数指针数组的应用
数组是存放相同类型数据的存储空间,我们已经学习了指针数组。
如:
int*arr[10] 表示存放十个整型指针的数组arr
所以我们可以打开猜想一下,函数指针数组应该是什么样的呢?
1.2函数指针数组的应用
//函数指针数组的用途: 转移表(相当于一个中介,连接一个函数到另一个函数)
如何转换,转换的是什么?步骤是什么?
演示:
二、指向函数指针数组的指针
指向函数指针数组的指针是一个指针
指针指向一个数组,数组的元素都是函数指针。
void test(const char* str) { printf("%s\n", str); } int main() { //函数指针pfun void (*pfun)(const char*) = test; //函数指针的数组pfunArr void (*pfunArr[5])(const char* str); pfunArr[0] = test; //指向函数指针数组pfunArr的指针ppfunArr void (*(*ppfunArr)[5])(const char*) = &pfunArr; //(*ppfunArr) ppfunArr与*先结合,说明这是一个指针,剩下void (*[5])(const char*) 实际上是 void (*)(const char*) [5]函数指针数组 //所以 void(*(ppfunArr)[5])(const char*) 这是个指向函数指针数组的指针 return 0;
我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针,确定或者使用这一类的指针数组、数组指针,第一步是确认ppfunArr先和谁结合,确定到底是数组还是指针,然后向外刨析
我们可以发现,(ppfunArr)与【】先结合的时候这是函数指针数组,当与*先结合的时候,这就是指向函数指针数组的指针
判别这一类类型的方法:
1.确认ppfunArr先和谁结合,确定到底是数组还是指针
2.如果是【】说明这个整体是数组,如果是 * 说明整体是指针
3.然后向外刨析,一个括号一个括号的破开
4.void(*pa)()实际上是 void(*)() pa 前者为函数类型,后者pa是指针名
三、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
#include<stdio.h> int sub(int x, int y) { return x - y; } int add(int x, int y) { return x + y; } int mul(int x, int y) { return x * y; } int div(int x, int y) { return x / y; } void coul(int(*pa)(int, int)) { int x, y; scanf("%d %d", &x, &y); int len=pa(x, y); printf("%d\n", len); } int main() { //函数指针 pa int(*pa)(int, int) = add; //函数指针数组 int(*ppa[10])(int, int) = { add,sub,mul,div }; //ppa[10] ppa 先于[]相结合 说明这个是个数组 剩下元素为 int(*)(int,int)说明存放的是函数指针类型 //所以这就是可以存放十个以两个形参为int类型,返回类型为int的,函数指针数组,ppa(数组名) //对于函数指针数组的应用 int x, y; int input = 1; int ret = 0; do { printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf("*************************\n"); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: coul(add); //使用回调函数,使得main函数与要实现的add或者sub等函数建立起来桥梁 break; case 2: coul(sub); break; case 3: coul(mul); break; case 4: coul(div); break; case 0: printf("退出程序\n"); break; default: printf("选择错误\n"); break; } } while (input); return 0; }
3.1qsort函数
3.1.1qsort函数的定义
qsort函数的使用,需要 <stdio.h>和<search.h>两个头文件
需要传递的数据为:
1.void* base 目标数据(需要排序的)
2.size_t num 排序的数据的元素个数,如arr[10],就传参为10
3.size_t width 排序数据的元素所占字节的大小,如 int arr[10],传参为4
4.int (__cdecl *compare )(const void *elem1, const void *elem2 )
传递的是函数指针,实参为两个被const修饰的无符号指针类型,void*是为了普适性,可 以接收所有类型的数据
接下来是代码实现:
#include<stdio.h> #include<search.h> int compare(const void* e1, const void* e2) { //使用 const是防止修改e1,e2的指针指向的地址,void*(无符号指针)表示函数的普适性 // 可以接收所有类型的指针。 return *(int*)e2 - *(int*)e1; //(int*)是因为是对void*类型进行强制转换,然后*解引用,比较e1 和 e2 的大小 } int main() { int arr[] = { 1,2,31,45,55,6,7,8,9,10,0 }; int sz = sizeof(arr) / sizeof(arr[0]); qsort(arr, sz, sizeof(arr[0]), compare); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
3.1.2使用回调函数,模拟实现qsort(冒泡的方式)
//使用回调函数,模拟实现qsort(采用冒泡的方式) //比较函数 int compare(const void* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } //交换函数 void swap(void* e1, void* e2, int width) { for (int i = 0; i < width; i++) { //一个元素 width个字节,自然是width次转换 char tmp = (*((char*)e1 + i)); (*((char*)e1 + i)) = (*((char*)e2 + i)); (*((char*)e2 + i)) = tmp; } } //使用冒泡排序的方式模拟实现qsort函数 void buffer_qsort(void* base, size_t num, size_t width, int (*compare)(const void* e1, const void* e2)) { for (int i = 0; i < num-1; i++) { for (int j = 0; j < num - 1 - i; j++) { //冒泡排序基本的循环是不用变的,只需要改变交换的方式 if (compare((char*)base + j * width, (char*)base + (j + 1) * width) > 0) { //传递数据的适合因为不知道传参的是什么类型,所以就是用表示一个字节的char*进行强制类型转换 //(char*)base 取的是数组的首元素的第一个字节,然后加上j*width,相当于跳过该类型的元素,找到下一个元素 //进行compare函数比较 //swap函数进行交换 swap((char*)base + j * width, (char*)base + (j + 1) * width,width); //除了传递每一个元素(char*)base + j * width 也要传递一个数组元素字节为多少 width //我们的交换原理就是创建一个临时变量 width次循环交换 } } } } int main() { int arr[] = { 11,2,3,4,45,62,7,8,8,9 }; int sz = sizeof(arr) / sizeof(arr[1]); buffer_qsort(arr, sz, sizeof(arr[1]), compare); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
3.1.3qsort算法的实现
具体介绍,请看博主主页快排那一章的内容,下面仅仅是演示快排算法
void quick_sort(int* arr, int l, int r) { if (l >= r) { return; } int k = arr[l]; int i = l - 1; int j = r + 1; while (i < j) { do { i++; } while (arr[i] < k); do{ j--; } while (arr[j] > k); if (i < j) { int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; } } quick_sort(arr, l, j); quick_sort(arr, j + 1, r); } int main() { int arr[] = { 1,25,3,4,5,6,7,8,9,10 }; int sz = sizeof(arr) / sizeof(arr[0]); quick_sort(arr, 0, sz-1); for (int i = 0; i < sz; i++) { printf("%d ", arr[i]); } return 0; }
总结
那么到此为止,指针进阶的内容就结束啦,完结撒花!!!
我们在这边学习了,字符数组、指针数组、数组指针、函数指针、函数指针数组、指向函数指针数组的指针,以及回调函数的定义,和通过冒泡排序模拟实现qsort函数,讲解qsort函数的原理,以及对于quick_sort快排算法的补充。
博主提前预告哦,接下来是对于字符串的一系列函数的学习,让我们拭目以待!!!