* 指针进阶 *

字符指针:指向字符的指针,存放的是字符变量的地址char *p = "abcdef"
printf %c 是输出字母a,因为存储的是字符串首字符的地址 。
printf %s 是输出整个字符串
“abcdef”是常量字符串,不能修改(*p = ‘w’)//err,
数组指针: 是指向数组的指针 。数组指针类型 int (*)[10]一个指针存一个地址
int* p1 [10]p1与[]结合,是数组,存放指针的数组
int (*p2)[10]p2与*结合,是指针,指向数组,指向的数组是十个元素,每个元素的类型是int
int(*parr3[10])[5]parr3是个数组,数组有10个元素,每个元素的类型是int(*)[5] 。Parr3是存放数组指针的数组

如何定义指针变量eg: char* arr[5]
char* (*p)[5] = &arr
分析:首先p要存数组的地址,它要是个指针,所以先(*p),接着要让这个指针指向这个数组,故变为(*p)[5]。指向的该数组每个元素的类型是char *,所以最终为char* (*p)[5]
把数组名字(和[])去掉就是数组元素的类型

数组名理解:
通常情况下数组名说的是数组首元素的地址,但有两个例外:
1.sizeof(数组名)
2.&数组名(详细见初始数组那篇博客)
数组指针应用: void print1(int (*p)[10], int sz){ int i = 0; for (i = 0; i < sz; i++) {//*p 相当于数组名,数组名又是首元素的地址,所以*p就是&arr[0]printf("%d ", *(*p + i)); } printf("\n");}int main(){ int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; //写一个函数打印arr数组的内容 int sz = sizeof(arr) / sizeof(arr[0]); // print1(&arr, sz); return 0;}
这里强行使用数组指针接受参数,不如之前利用指针直接接受来的方便,所以不推荐这种写法 。所以数组指针一般不用于一维数组里 。
下面使用二维数组来演示数组指针普通二维数组传参
void print2(int arr[3][5], int c, int r){ int i = 0; for (i = 0; i < c; i++) {int j = 0;for (j = 0; j < r; j++){printf("%d ", arr[i][j]);}printf("\n"); }}void print2(int arr[3][5], int c, int r){ int i = 0; for (i = 0; i < c; i++) {int j = 0;for (j = 0; j < r; j++){printf("%d ", arr[i][j]);}printf("\n"); }} 下面是用数组指针传参
void print2(int(*p)[5], int c, int r){ int i = 0; for (i = 0; i < c; i++) {int j = 0;for (j = 0; j < r; j++){//p+i是指向第i行的//*(p+i)相当于拿到了第i行,也相当于第i行的数组名//数组名表示首元素的地址,*(p+i) 就是第i行第一个元素的地址printf("%d ", *(*(p + i) + j));//printf("%d ", p[i][j]);}//arr[i]//*(arr+i)////arr[i][j]//*(arr+i)[j]//*(*(arr+i)+j)printf("\n"); }}int main(){ int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; //int (*ptr)[3][5] = &arr; //写一个函数,打印arr数组 print2(arr, 3, 5); return 0;}
二维数组数组名表示的是第一行的地址,即5个整型的一维数组,因为数组本身是连续存放的,p+1 直接跳过一行数组来到第二行,将二维数组降维传变为一维数组(一行一行接收)
数组下标访问:
arr [i]<——> *(arr+i)等价
arr [i][j]* (arr+i) [j]*(*(arr+i)+j)等价
上图为存放数组指针的存储方式
数组传参 一维数组传参,当形参写成数组的形式时,形参部分数组大小可以省略void test(int arr[]) (数组名传参的时候,传过去的是数组首元素地址,形参没有创建数组)当形参写成指针的形式时void test(int *p)
下面我们进行一个反推,已知形参用整型一级指针来接收,我们思考一下实参可以写什么?
1.一个元素的地址 。因为指针本身就是用来存储地址
2.一个一级指针 。用指针接受指针无可厚非 。
3.一个一维数组 。将数组名传过去,数组名表示首元素地址,用指针接收也非常合适 。
注意:这里不能将二维数组传过去,因为二维数组名表示第一行数组的地址,无法用一个指针接收
二级指针传参与上面同理:
1.使用二级指针
2.将一级指针进行&在进行传参
3.传指针数组(下图例子为char*类型的指针数组,形参用char**接收)



test1函数接收的是二维数组第一行的地址
test2函数接收的是整个二维数组的地址 。
函数指针:指向函数的指针两种写法都能打印出函数的地址,表示的意义相同 。(这里与arr,&arr区分开)
(*pf)是个指针,指向函数参数int类型,返回类型是int

将Add赋值给pf,说明pf和Add等价 。所以*加不加无所谓,都可以实现加法运算 。但是一旦加上*符号,要用括号括起来 。函数指针的类型位int(*)(int,int)

调用0地址处的函数(假设0是个地址,将0强制转换为函数进行访问,操作系统内部有权限访问)


【* 指针进阶 *】对于一个函数声明,需要交待清楚函数返回类型,函数名,参数

函数指针数组 在函数指针的基础上加个 [4] ,就变为函数指针数组 。注意:函数参数类型与函数返回类型必须一致,因为数组是存放一组相同类型元素的集合
函数指针数组的应用——计算器 void menu(){ printf("**************************\n"); printf("****1.add2.sub****\n"); printf("****3.mul4.div****\n"); printf("****0.exit****\n"); printf("**************************\n");}int main(){ int input = 0; int x = 0; int y = 0; int ret = 0; //转移表 int (*pfArr[])(int, int) = {0, Add, Sub, Mul, Div}; do {menu();printf("请选择:>");scanf("%d", &input);if (input == 0){printf("退出计算器\n");}else if(input >= 1 && input<=4){printf("请输入2个操作数:>");scanf("%d%d", &x, &y);ret = pfArr[input](x, y);printf("ret = %d\n", ret);}else{printf("选择错误\n");} } while (input); return 0;}
这里的 函数指针数组 的作用叫做转移表,起到了跳转的作用 。但也有一定的局限性,只有当这些函数参数返回值类型一致的时候,使用函数指针数组才简便 。

指向函数指针数组的指针
指向函数指针数组的指针的过程
回调函数回调函数就是一个通过函数指针调用的函数 。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数 。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应 再次用计算器运用回调函数,减少函数冗余,这里用到calc(计算函数) 。不用多余的写scanf
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;}void menu(){ printf("**************************\n"); printf("****1.add2.sub****\n"); printf("****3.mul4.div****\n"); printf("****0.exit****\n"); printf("**************************\n");}void calc(int (*pf)(int,int)){ int x = 0; int y = 0; int ret = 0; printf("请输入2个操作数:>"); scanf("%d%d", &x, &y); ret = pf(x, y); printf("ret = %d\n", ret);}int main(){ int input = 0; do {menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break; case 0:printf("退出计算器\n");break;default:printf("选择错误\n");break;} } while (input); return 0;}
qsort函数(msdn上查找)
void qsort( void *