一、回顾
在这之前,我们学习了很多关于指针的内容,我们先在这里简单的回顾一下。
1、一级指针
int* p; -- 整形指针-指向整形的指针
char* p; ...
void* p;...
...
2、二级指针
int** p;
char** p;
...
3、数组指针 -- 指向数组的指针
int (*p)[ ];
int main(){int arr[3] = {1,2,3};int (*parr)[3] = &arr;
return 0;
}
4、指针数组 -- 存放指针的数组,本质就是数组。
int* arr[ ];
int main(){int a[] = {1,2,3,4,5};int b[] = {2,3,4,5,6};int c[] = {3,4,5,6,7};int* arr[] = {a,b,c};for(int i=0;i<3;i++){for(int j=0;j<5;j++){printf("%d ",*(arr[i] + j));}printf("\n");}return 0;
}
二、函数指针
1、理解
指向函数地址的指针
2、写法
函数返回类型 (* p)(参数1、参数2、...)
3、举例
int Add(int x,int y){return x + y;
}
int main(){int (*p)(int ,int ) = &Add;printf("%p\n",&Add);printf("%p\n",Add);return 0;
}
在前面一节中将到了 数组名和&数组名的区别、但在这里 函数名 == &函数名。
4、使用
由于 函数名 == &函数名。所以可以有多种调用方法。
int Add(int x,int y){return x + y;
}
int main(){int (*p)(int ,int ) = &Add;int ret = (*p)(20,6);
// int ret = p(20,6);
// int ret = Add(20,6);printf("%d\n",ret);return 0;
}
5、试解析下列的一段代码
(*(void ( * ) () ) 0 ) ( ) ;
三、函数指针数组
1、理解
存放函数指针的数组、存放同类型的函数指针。
2、写法
函数返回类型 (* pArr [ ])(参数1、参数2、...)
3、举例 -- 简单计算器的实现
void menu(){printf("***********************\n");printf("*****1、Add 2、Sub**\n");printf("*****3、Mul 4、Div**\n");printf("***** 0、exit **\n");printf("***********************\n");
}
int Add(int x,int y){return x + y;
}
int Sub(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;
}
int main(){int input = 0;do{int x = 0, y = 0, ret = 0;int (*parr[5])(int, int) = {NULL,Add,Sub,Mul,Div};menu();printf("请选择要实现的功能:");scanf("%d",&input);if(input >= 1 && input <=4){printf("请输入两个操作数:");scanf("%d %d",&x , &y);ret = (*parr[input])(x,y);printf("ret=%d\n",ret);}else if(input == 0){printf("退出程序\n");break;}else{printf("输入错误,重新输入\n");}}while(input);return 0;
}
4、* 指向函数指针数组的指针
void test(const char* str ){printf("%s\n",str);
}
int main(){void (*p)(const char*) = test; //函数指针pvoid (*parr[2])(const char*) = { test , NULL}; //函数指针数组parrvoid (*(*p3)[2])(const char*) = &parr;// 指向函数指针数组的指针return 0;
}
四、回调函数
1、概念理解
通过函数指针调用的函数。 --- 把一个函数的地址(指针)作为参数传递给另一个函数,当这个指针被用来调用其所指的函数时,就称它为回调函数。
2、举例说明 -- 库函数 qsort 的使用
#include<stdio.h>
#include <stdlib.h> // qsort头文件
// qsort 函数
// void qsort (void* base, //指向待排序的首元素地址。
// size_t num, //待排序的元素个数
// size_t size, //待排序元素的大小,单位时字节
// int (*compar)(const void*,const void*)); //待排序元素的比较方式int compare (const void * a, const void * b)
{return ( *(int*)a - *(int*)b );
}
int main ()
{int arr[] = {14,78,24,69,10};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、举例 -- 模拟qsort实现冒泡排序升级版
#include<stdio.h>
// 函数实现
void swap(char* a , char* b , int size){for(int i=0;i<size;i++){char tmp = *a;*a = *b;*b = tmp;a++;b++;}
}
void bubblePP(void* base, //指向待排序的首元素地址int num, //待排序的元素个数int size, //待排序元素的大小,单位是字节int (*compar)(const void* ,const void* )){ //待排序元素的比较方式 for(int i=0;i<num ;i++){for(int j=0;j<num - i - 1;j++){if(compar((char*)base +j*size , (char*)base + (j+1)*size) > 0){swap((char*)base +j*size , (char*)base + (j+1)*size , size);}}}
}// 用户输入
int compar (const void * a, const void * b)
{return ( *(int*)a - *(int*)b );
}void print(int arr[], int sz){for(int i=0;i<sz;i++){printf("%d ",arr[i]);}printf("\n");
}
void test1(){int arr[] = {14,78,24,69,10};int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz); //排序之前打印bubblePP(arr,sz,sizeof(arr[0]),compar); // 排序print(arr, sz); //排序之后打印
}
//void test2(){
// char arr[] = {'w','m','z','d'};
// int sz = sizeof(arr) / sizeof(arr[0]);
// print(arr, sz); // 要使 compar 和 print 的类型参数与这里的相对应
// bubblePP(arr,sz,sizeof(arr[0]),compar); // 排序
// print(arr, sz);
//}
int main(){test1();
// test2();return 0;
}
要点分析:
在函数内部,并不知道用户传递给我们的数据类型,所以根据最小的char类型以及待排序的元素大小size,来确定下一个元素的位置。
这里依然是和上面一样的问题,所以在swap交换的时候采用一字节一字节的交换方式,并以元素大小size为限制表示一个元素是否交换完成。