函数指针本质上是指针,是一个指向函数的指针。函数都有一个入口地址,所谓指向函数的入口地址。(这里的函数名就是代表入口地址)
函数指针存在的意义:
语法:
返回值类型 (*指针变量名)(形参列表);
举例
//普通函数
int add(int a,int b){...}
//函数指针
int (*p)(int a,int b);
①定义的同时赋值
//函数指针需要依赖于函数,先有函数,后有指针
//定义一个普通函数
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的入口地址赋值给指针
注意:
1.函数指针指向的函数要和函数指针定义的返回值类型,形参列表对应,否则编译报错
2.函数指针是指针,但不能指针运算,如p++等,没有实际意义
3.函数指针作为形参,可以形成回调
4.函数指针作为形参,函数调用时的实参只能是与之对应的函数名,不能带小括号0
5.函数指针的形参列表中的变量名可以省略
注意:函数不能作为函数的形参,但是指向函数的函数指针是可以作为函数的形参的。
/*************************************************************************
> File Name: demo01.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 10时26分13秒
************************************************************************/
#include
/**
* 定义一个函数,求两个数的最大值
*/
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;
// 间接调用函数:方式1
max = p(a,b);//直接将指针名作为函数名
printf("%d,%d中的最大值是%d\n",a,b,max);
// 间接调用函数:方式2
max = (*p)(a,b);//间接将指针名作为函数名
printf("%d,%d中的最大值是%d\n",a,b,max);
return 0;
}
定义
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针备用来调用其所指向的函数时。我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。
简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便 该函数在处理相似事件的时候可以灵活的使用不同的方法。
/*************************************************************************
> File Name: demo02.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 10时56分06秒
************************************************************************/
#include
/**
* 回调函数1
*/
int callback_1(int a)
{
printf("hello,this is call_back_1:a=%d\n",a);
return a;
}
/**
* 回调函数2
*/
int callback_2(int b)
{
printf("hello,this is call_back_2:a=%d\n",b);
return b;
}
/**
* 实现回调函数
*/
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);
return 0;
}
运行结果
本质上是函数,这个函数返回值类型是指针,这个函数称之为指针函数。
语法:
//写法1
数据类型* 函数名(形参列表);
{
函数体;
return 指针变量;
}
//写法2
数据类型 *函数名(形参列表);
{
函数体;
return 指针变量;
}
举例:
int *get(int a)
{
int *p = &a;
return p;
}
int main()
{
int *a = get(s);
printf("%d\n",*a);
}
注意:
在函数中不要直接返回一个局部变量的地址,因为函数调用完毕后,局部变量会被回收,使得返回的地址就不明确,此时返回的指针就是野指针。
解决方案:
如果非要访问,可以给这个局部变量添加(定义的时候添加) static ,可以延长它的生命周期,从而避免野指针(尽量少用,因为存在内存泄漏)
演示案例:
/*************************************************************************
> File Name: demo03.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 14时23分41秒
************************************************************************/
#include
int *add(int a,int b)
{
static int sum;//使用static修饰局部变量,会提示生命周期,但是不推荐
sum = a + b;
return ∑//执行完成return 作为函数作用域的布局变量sum的空间被释放
}
int main(int argc,char *argv[])
{
int *res = add(3,4);//接受到了地址,但是地址对应的空间已经释放
printf("%d\n",*res);
return 0;
}
需求:有若干个学生,每个学生有4门成绩,要求用户输入学号(int id)后,能输出该学生的全部成绩(float scores[4]),用指针函数实现。
代码:
/*************************************************************************
> File Name: demo04.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 14时39分35秒
************************************************************************/
#include
/**
* 定义一个函数,要求输入学生序号,返回该学生所有成绩
* @param all:所有人的成绩
* @param id: 要检索学生的序号
* @rwturn id对应学生的成绩数数组(指针)
*/
float *search(float (*all)[4],int id)
{
//定义一个指针变量,用来接收查询的某个学生的所有成绩
float *pt;
pt = *(all +id);//行偏移
return pt; //赋值运算中:float pt[4] == float *pt;
}
int main(int argc,char *argv[])
{
//准备一个二维数组,存放3个学生的成绩
float scores[][4]={{66,77,88,99},{60,70,80,90},{61,71,81,91}};
// 定义一个
int m;
printf("请输入学生序号(0~2):\n");
scanf("%d",&m);
printf("第%d个学生的成绩为\n",m);
float *p;
p = search(scores,m);
for(;p < scores[m] +4;p++)
{
printf("%5.2f\t",*p);
}
printf("\n");
return 0;
}
二级指针(多重指针)用于存储一级指针的地址,需要两次解引用才能访问原始数据。其他多级指针的用法类似,但实际开发最常见的多级指针是二级指针。
int a = 10; //a是普通变量
int *p = &a; //一级指针(p指向a,p存储的是a的地址
int **w = &p; //二级指针(w指向p,w存储的是p的地址
int ***x= &w; //三级指针(x指向w,x存储的是w的地址
数据类型 **指针变量名 = 指针数组的数组名 | 一级指针的地址
①与指针数组等效二级指针与指针数组等效,但二维数组不等效。二维数组名实在数组指针类型,如int (*)[3]
而非二级指针。
//指针数组
int arr[] ={11,22,33};
int *arr_[] = {&arr[0],&arr[i],&arr[2]}//正确的指针数组的定义
//二级指针接受指针数组
chae* str[3] = {"abc","aaaa034","12a12"};//str存储的是三个字符串的首地址
char **p = str;
②与二维数组的差异二维数组名是数组指针类型,直接赋值给二级指针会导致类型不匹配
int arr[2][3] = {{1,3,5},{11,33,55}};
int (*p)[3] = arr;//arr这个数组名就是数组指针类型 int a = 10; int b = a;
a == b;
int **k = arr; //编译报错,arr类型 int(*)[3] 不兼容 k类型 int**
/*************************************************************************
> File Name: demo06.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 15时49分15秒
************************************************************************/
#include
void fun1()
{
// 定义一个字符类型的指针数组
char *arr[] = {"orange","apple","grape","banana","kiwi"};
int len = sizeof(arr)/sizeof(arr[0]);
for(int i = 0;i <len ;i++)
{
printf("%s\n",arr[i]);
}
printf("\n");
}
void fun2()
{
char *arr[] = {"orange","apple","grape","banana","kiwi"};
// 二级指针等效与指针数组
int len = sizeof(arr) /sizeof(arr[0]);
char **p = arr;
for(int i = 0;i <len ;i++)
{
//printf("%s\n",p[i]); //下标法
printf("%s\n",*(p + i));//指针法
}
printf("\n");
}
void fun3()
{
char *arr[] = {"orange","apple","grape","banana","kiwi"};
// 二级指针等效与指针数组
int len = sizeof(arr) /sizeof(arr[0]);
char **p;
int i = 0;
do
{
p = arr + i;
printf("%s\n",*p);
i++;
}while(i<len);
printf("\n");
}
int main(int argc,char *argv[])
{
fun1();
fun2();
fun3();
return 0;
}
②其他类型的二级指针需要两次解引用访问数据,常用操作指针数组
/*************************************************************************
> File Name: demo07.c
> Author: 刘孟丹
> Description:
> Created Time: 2025年05月22日 星期四 15时42分00秒
************************************************************************/
#include
int main(int argc,char *argv[])
{
//普通的一维数组
int arr1[] = {11,22,33,44,55,66};
// 创建一个指针数组
int *arr[] = {&arr1[0],&arr1[1],&arr1[2],&arr1[3],&arr1[4],&arr1[5]};
// 用一个二级指针接收指针数组
int **p = arr;
// 遍历数组
for(int i = 0;i < sizeof(arr) /sizeof(arr[0]);i++)
{
printf("%-6d",*p[i]);
printf("%-6d",**(p+i));
}
printf("\n");
return 0;
}
① 二级制指针与指针数组等效,可简化指针数组的遍历操作。
② 二维数组名是数组指针类型(如: int (*)[3]
),与二级指针(int**
)类型不兼容。