本章章节概述:
1、指针函数
2、函数指针
3、指针函数和函数指针的核心区别
4、回调函数
C语言指针进阶:指针函数与函数指针;
指针是C语言中最强大但也最容易混淆的特性之一。我在前面章节已经讲解了指针的基础用法,数组指针与指针数组的区别,今天我们来深入辨析另一组极易混淆的概念-指针函数和函数指针,并讲解基于函数指针实现的回调函数使用方法。这两个概念名字只有语序不同,但本质天差地别,是C语言进阶的核心难点之一。
一、指针函数
指针函数,从名字拆解:"指针" 是修饰词,核心主体是 "函数"。也就是说,指针函数本质上是一个函数,只不过它的返回值类型是一个指针(内存地址)。
示例:
cpp
int *pfun(int a, int b);
概念:返回值为指针的函数,叫做指针函数。
作用:用于在函数中返回内存地址,将堆内存、静态变量或全局变量的地址返回给调用者,实现跨函数的内存操作与数据传递。
注意:指针函数绝对不能返回局部变量的地址!因为局部变量在函数执行结束后会被系统自动回收,返回的地址会变成悬空指针,访问它属于未定义行为,会导致程序崩溃。
代码使用示例:
cpp
#include "stdio.h"
int *find_max(int arr[], int len); //指针函数声明:返回int类型的指针
int main() //主函数
{
int num[]={1,2,3,4,5,66,7,8,9,10};
int len_long = sizeof(num) / sizeof(int);
int *max = find_max(num, len_long);
printf("数组中最大的值为= %d\n", *max);
printf("最大值所在的内存地址为:%p\n", max);
return 0;
}
int *find_max(int arr[], int len) //指针函数:返回值指针,形参数组和变量,功能数组中找最大值
{
int *max_p = &arr[0];
for(int i = 1; i < len; i++)
{
if(arr[i] > *max_p)
{
max_p = &arr[i]; //更新获取最大值的地址
}
}
return max_p;
}
==========================================
[Running] cd "c:\Users\LiYongFeng\Desktop\Test-C\" && gcc test1.c -o test1 && "c:\Users\LiYongFeng\Desktop\Test-C\"test1
数组中最大的值为= 66
最大值所在的内存地址为:0061FF04
[Done] exited with code=0 in 1.291 seconds
核心要点:指针函数的核心是函数,它的特殊之处只在于返回值是指针,而非常量数值。
二、函数指针
函数指针,从名字拆解:"函数" 是修饰词,核心主体是 "指针"。也就是说,函数指针本质上是一个指针变量,只不过它指向的不是普通变量,而是一个函数的入口地址。
示例:本质是指针,指向函数入口地址的指针,叫做函数指针。
cpp
int (*fptr)(int, int);
注意:只有返回值类型、参数个数、参数类型、参数顺序完全一致的函数,才能被同一个函数指针指向。
作用:利用函数指针,可以实现动态调用函数、将函数作为参数传递,是实现回调机制、多态逻辑的基础。
代码使用示例:
cpp
#include "stdio.h"
int add(int a, int b); // 函数声明
int sub(int a, int b); // 函数声明
int main()
{
int (*p_func)(int, int); // 函数指针声明
int i=10, j=20;
p_func = add;
printf("%d + %d = %d\n", i, j, p_func(i, j));
p_func = sub;
printf("%d - %d = %d\n",i,j, p_func(i, j));
return 0;
}
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
======================================
[Running] cd "c:\Users\LiYongFeng\Desktop\Test-C\" && gcc test1.c -o test1 && "c:\Users\LiYongFeng\Desktop\Test-C\"test1
10 + 20 = 30
10 - 20 = -10
[Done] exited with code=0 in 0.364 seconds
三、指针函数和函数指针的核心区别
很多人分不清这两个概念,核心就是搞不清「谁是主体」。我们从本质、语法、用途三个维度彻底区分开。
1. 本质不同
- 指针函数:本质是函数,和普通函数地位一样,只是返回值是指针。
- 函数指针:本质是指针变量,和普通指针地位一样,只是指向的是函数地址。
2. 声明语法不同(最直观区分标志)
关键看*和函数名有没有被括号括起来,注意:括号是灵魂!*和变量名被括号包起来的,就是函数指针;没有括号的,就是指针函数。
| 概念 | 声明示例 | 结合规则 |
|---|---|---|
| 指针函数 | int *func(int); |
()优先级高于*,func 先和 () 结合,说明是函数;返回值类型是 int* |
| 函数指针 | int (*p)(int); |
括号改变优先级,p 先和 * 结合,说明是指针;指针指向 int (int) 类型的函数 |
3. 用途不同
- 指针函数:用于返回内存地址,比如返回动态分配的内存首地址、返回数组中的元素地址等。
- 函数指针:用于动态调用函数、作为函数参数传递、实现回调机制、状态机、函数跳转表等。
四、回调函数的使用
回调函数是 C 语言中非常重要的编程机制,它的底层实现完全依赖于函数指针。
概念:如果把一个函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用它所指向的函数时,我们就称这个被调用的函数为回调函数(Callback Function)。
简单理解:回调函数不是由该函数的实现方自己直接调用,而是由另外一个 "框架函数" 在特定时机或条件下,通过传进来的函数指针间接调用。
作用:
- 解耦:通用逻辑和具体业务分离,框架不用关心底层具体实现。
- 灵活扩展:可以在不修改框架函数的前提下,新增不同的处理逻辑。
- 异步通知:常用于事件触发、中断处理、定时任务等场景。
回调函数的使用步骤
- 约定回调函数的参数和返回值格式;
- 实现符合格式的回调函数(可以有多个);
- 定义框架函数,接收函数指针作为参数;
- 调用框架函数时,将回调函数的地址传入。
cpp
#include "stdio.h"
void print_app1(int num); //打印形参函数1
void print_app2(int num); //打印形参函数2
void array_traversal(int arr[], int len, void (*func)(int));
int main()
{
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(int);
printf("原数组为:\n");
array_traversal(arr, 5, print_app1);
printf("乘二后的数组为:\n");
array_traversal(arr, 5, print_app2);
return 0;
}
void print_app1(int num) //打印形参函数
{
printf("app1: %d\n", num);
}
void print_app2(int num) //打印形参*2函数
{
printf("app2: %d\n", num*2);
}
//框架函数 :通用数组遍历
//第三个形参就是完整的函数指针类型:void (*func)(int)
void array_traversal(int arr[], int len, void (*func)(int))
{
for(int i = 0; i < len; i++)
{
func(arr[i]); //通过函数指针调用回调函数,处理当前元素
}
printf("\n");
}
制作不易!喜欢的小伙伴给个小赞赞!喜欢我的小伙伴点个关注!有不懂的地方和需要的资源随时问我哟!