15-C语言:第15~16天笔记
函数指针
定义
函数指针本质上是指针,是一个指向函数的指针。函数都有一个入口地址,所谓指向函数
的指针,就是指向函数的入口地址。(这里的函数名就代表入口地址)
函数指针存在的意义:
- 让函数多了一种调用方式
- 函数指针可以作为形参,可以形式调用(回调函数)
遵循:先有函数,后有指针
语法:
返回类型 (*指针变量名)(形参列表);
举例:
int (*fun)(int a, int b);int (*fun)(int, int); // 有点类似于函数声明
函数指针的初始化
① 定义的同时赋值
// 函数指针需要依赖于函数,先有函数,后有指针
// 定义一个普通函数
int add(int a, int b) { return a + b; }
// 定义一个函数指针,并初始化
// 观察:函数指针的返回类型和指向的函数的返回类型一致,函数指针的形参个数、类
型、位置和指向的函数的参数的一致。
int (*p)(int a, int b) = add; // 函数指针p指向函数add,这里的add不能带(),
add就表示函数的入口地址
② 先定义,后赋值
// 定义一个普通函数int add(int a, int b) { return a + b; }int (*p)(int, int); // 形参列表的参数名可以省略不写p = add; // 此时是将add的入口地址赋值给指针p
注意:
- 函数指针指向的函数要和函数指针定义的返回值类型,形参列表对应,否则编
译报错
- 函数指针是指针,但不能指针运算,如p++等,没有实际意义
- 函数指针作为形参,可以形成回调
- 函数指针作为形参,函数调用时的实参只能是与之对应的函数名,不能带小括
号()
- 函数指针的形参列表中的变量名可以省略
注意:函数不能作为函数的形参,但是指向函数的函数指针是可以作为函数的形参的。
案例
需求:求a,b两个数的最大值
代码:
#include <stdio.h>/**\* 定义一个函数,求两个数的最大值*/int get_max(int a, int b){return a > b ? a : b;}int main(int argc,char *argv[]){// 定义测试数据int a = 3, b = 4, max;// 直接调用函数max = get_max(a, b);printf("%d,%d中的最大值是:%d\n",a,b,max);// 定义一个函数指针int (*p)(int, int) = get_max;// 间接调用函数:方式1max = p(a,b);printf("%d,%d中的最大值是:%d\n",a,b,max);// 间接调用函数:方式2max = (*p)(a,b); // () > [] > *printf("%d,%d中的最大值是:%d\n",a,b,max);return 0;
}
回调函数
定义
==回调函数就是一个通过函数指针调用的函数==。如果你把函数的指针作为参数传递给另
一个函数,当这个指针被用来调用其所指向的函数时。我们就说这是回调函数。回调函数
不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,
用于对该事件或条件进行响应。
简单来说,就是使用函数指针作为函数的形参,这种函数就被称作回调函数。
为什么要用回调函数
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存
在一个具有特定原型和限制条件的被调用函数。
简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函
数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
案例
#include <stdio.h>
/**
\* 回调函数1*/int callback_1(int a){printf("hello, this is callback_1:a=%d\n", a);return a;}/**\* 回调函数2*/int callback_2(int b){printf("hello, this is callback_2:b=%d\n", b);return b;}/**\* 回调函数3*/int callback_3(int c){printf("hello, this is callback_3:c=%d\n", c);return c;}/**\* 实现回调函数(形参是函数指针的函数)*/int handle(int x, int (*callback)(int)){printf("日志:开始执行任务!\n");int res = callback(x);printf("日志:执行结果:%d\n",res);printf("日志:结束执行任务!\n");}int main(int argc,char *argv[]){handle(100,callback_1);handle(200,callback_2);handle(300,callback_3);return 0;}
运行结果:
二级指针
定义
二级指针(多重指针)用于储存一级指针的地址,需要两次解引用才能访问原始数据。其
他多重指针的用法类似,但实际开发中最常见的指针是二级指针。
int a = 10; // a是普通变量,也就是原始数据
int *p = &a; // 一级指针,p指向a,解引用1次就可以获取a的值
printf("%d\n", *p); // 10int **w = &p; // 二级指针,w指向p,解引用2次就可以获取a的值
printf("%d\n", **w);// 10int ***k = &w; // 三级指针,k指向w,解引用3次就可以获取a的值
printf("%d\n", ***k); // 10 int a1 = ***k; int *a2 = **k; int **a3
= *k; int ***a4 = k;
语法
数据类型 **指针变量名 = 指针数组的数组名 | 一级指针的地址
特点
① 与指针数组的等效性 二级指针与指针数组在某些时候存在等效性,但与二维数组不等
效。二维数组名是数组指针类型,如 int (*)[3]
,而非二级指针。
// 指针数组
int arr[] = {11,22,33};
int *arr_[] = {&arr[0],&arr[1],&arr[2]};
// 二级指针接收指针数组
char *str[3] = {"abc","aaa034","12a12"};
char **p = str; // p:数组首地址,行地址,默认0行 *p:列地址,默认0行0列
**p:列元素
#include <stdio.h>
int main(int argc,char *argv[])
{char *str[3] = {"abc","aaa034","12a12"};char **p = str;// 打印字符串// for (int i = 0; i < 3; i++)// {// printf("%s\n", *p);// p++;// }// 打印字符int i = 0;while(**p != '\0'){printf("%-2c",**p);(*p)++;}printf("\n");return 0;
}
② 与二维数组的差异 二维数组名是数组指针类型,直接赋值给二级指针会导致类型不匹
配
// 数组指针可以指向一个二维数组
int arr[2][3] = {{1,3,5},{11,33,55}};
int (*p)[3] = arr;
// 二级指针不等效二维数组
int **k = arr; // 编译报错 arr类型 int(*)[3] 不兼容 k类型 int**
0’)
{
printf(“%-2c”,**p);
(*p)++;
}
printf(“\n”);
return 0;
}
② **与二维数组的差异** 二维数组名是数组指针类型,直接赋值给二级指针会导致类型不匹配
// 数组指针可以指向一个二维数组
int arr[2][3] = {{1,3,5},{11,33,55}};
int (p)[3] = arr;
// 二级指针不等效二维数组
int **k = arr; // 编译报错 arr类型 int()[3] 不兼容 k类型 int**