进阶指针( 三 ): 函数指针

一、函数指针的本质:指向 “代码” 的指针

什么是函数指针?

  • 定义:存储函数入口地址的指针
  • 核心特性
    • 函数名 == & 函数名
    • 数组名 ! = & 数组名
    • 直接用函数名 / 指针名调用函数(无需解引用)

一句话对比数组指针:

场景 数组指针 函数指针
指向内容 数据(数组) 代码(函数)
取地址差异 arr ≠ &arr Add == &Add
调用方式 需解引用 + 下标 直接当函数名用

二、定义函数指针:从 “迷路” 到 “开窍”

定义步骤(以Add函数为例):

  1. 原函数

    int Add(int x, int y) {  // 返回int,参数int,int  
        return x + y;  // 实现加法  
    }  
    
  2. 提取类型int (*)(int, int)(返回值 + 参数列表)
  3. 定义指针

    定义思路:
     // 1. 先写一个普通函数(返回int,参数int,int)  
    int Add(int x, int y) { return x + y; }  
    
    // 2. 定义函数指针(抄函数返回值+参数,套括号+*)  
    int (*pf)(int, int);  // ✨ 口诀:返回值 (*指针名)(参数)  
    
    // 3. 赋值(函数名=地址,直接抄!)  
    pf = Add;  // 等价于 pf = &Add;(&可省略,函数名天生是地址)  
    
    
    
    带箭头标注:
    int          // ← 和Add的返回值一致(都是int)  
    (*pf)       // ← 定义指针pf,*表示这是指针(⚠️括号必加!不然变成返回指针的函数)  
    (int, int)  // ← 抄Add的参数列表(数量/类型必须一模一样)  
    = Add;      // ← 直接赋值函数名,不用写&(函数名=地址,天生一对!)  

 3 个细节:

  • *pf的意义
    • 仅用于声明这是 “函数指针”,实际存储的是函数地址(无需解引用)
    • 类比:就像给遥控器贴标签 “这是控制电视的”,按按钮时直接生效
  • 括号不能省

    int *pf(int, int);  // ❌ 错误!这是“返回int*的函数”(叫“指针函数”,和函数指针没关系)  
    int (*pf)(int, int); // ✅ 正确!重点在“指针”,指向一个返回int、参数int,int的函数  
    
  • 赋值的灵活性

    pf = &Add;  // 取地址(规范写法)  
    pf = Add;   // 直接赋值(更常用,编译器知道函数名就是地址)  
    

三、调用函数指针:3 种方式 + 1 个陷阱

超简单调用示例(pf已指向Add):

// 方式1:直接当函数名用(推荐,简单到爆炸)  
int res1 = pf(3, 5);     // 输出8,和Add(3,5)完全一样!  
// 方式2:带解引用调用(新手友好,好理解)  
int res2 = (*pf)(3, 5);  // 输出8,加*只是“仪式感”,编译器会自动处理  
// 方式3:千万别这么写!  
int res3 = *pf(3, 5);    // ❌ 错误!等价于*(pf(3,5)),先调用函数再解引用返回值(返回值是int,解引用会报错)  

⚠️ 陷阱:

为什么*pf(3,5)会错?

  1. ()优先级比*高,所以先执行pf(3,5)(调用函数,返回 int)
  2. 对 int 类型结果解引用(比如*8),就像对 “数字 8” 按遥控器,完全没意义!

四、函数指针的应用:超简单回调函数

场景:做一个 “万能计算器”,支持加减乘除

// 1. 定义4个基础运算函数  
int Add(int a, int b) { return a + b; }  
int Sub(int a, int b) { return a - b; }  
int Mul(int a, int b) { return a * b; }  
int Div(int a, int b) { return a / b; }  

// 2. 定义函数指针(指向“带两个int参数、返回int”的函数)  
typedef int (*OpFunc)(int, int);  // 给函数指针类型起个外号叫OpFunc  

// 3. 万能计算函数(通过传入不同函数指针,实现不同运算)  
int Calculator(int x, int y, OpFunc op) {  
    return op(x, y);  // 用函数指针调用具体运算函数  
}  

// 4. 调用:传入不同运算函数  
int result1 = Calculator(10, 3, Add);   // 调用加法,结果13  
int result2 = Calculator(10, 3, Sub);   // 调用减法,结果7  
int result3 = Calculator(10, 3, Mul);   // 调用乘法,结果30  
int result4 = Calculator(10, 3, Div);   // 调用除法,结果3(整数除法)  

核心原理(类比快递员送包裹):

  • 你告诉Calculator:“我需要计算,具体用哪个函数你别管,我会给你”
  • 你通过函数指针op,把Add/Sub等函数 “快递” 给Calculator
  • Calculator收到后,直接用op(x,y)调用,实现 “算法不变,逻辑可变”

五、避坑指南

1. 函数指针≠指针函数(名字顺序很重要!)

定义形式 本质 一句话区别
int (*pf)(); 函数指针(指针是主体) “这是一个指针,指向函数”
int *pf(); 指针函数(函数是主体) “这是一个函数,返回指针”

2. 参数类型必须严格匹配

int Add(int, int);  
int (*pf)(double, double) = Add;  // ❌ 错误!参数类型要完全一样(int≠double)  

3. 先初始化再调用,拒绝 “野指针”

int (*pf)();  // 未初始化的函数指针(不知道指向哪里,像没插电的遥控器)  
pf(1, 2);     // ❌ 危险!可能让程序崩溃(指向乱码地址)  

4. 函数名 vs 数组名:唯一相同的 “地址”

表达式 数组名(int arr[10] 函数名(int Add()
表达式 首元素地址 函数入口地址
&表达式 整个数组地址 还是函数入口地址(和表达式一样!)

5. 函数指针的大小:和普通指针一样

printf("%d\n", sizeof(pf));  // 输出8(64位系统),因为只存地址,和函数多大没关系  

六、总结:函数指针核心公式

✅ 定义公式(抄作业专用):

返回值类型 (*指针名)(参数1类型, 参数2类型) = 函数名;
例:int (*pf)(int, int) = Add;(指向加法函数)

✅ 调用公式(两种写法任选):

指针名(参数1, 参数2);        // 推荐!简单直接,和普通函数调用一样  
(*指针名)(参数1, 参数2);     // 可选!加*更直观,适合新手理解  

✅ 回调函数公式(万能模板):

返回值类型 通用函数(参数, 函数指针名) {  
    return 函数指针名(参数);  // 用传入的函数指针执行具体逻辑  
}  

来几个题目试试手

  1. 定义一个函数指针,指向int Max(int, int)(求两数较大值),并调用它计算75的最大值
  2. 用函数指针改造你的计算器,新增一个Mod函数(求余数),并测试调用
  3. 思考:为什么函数指针赋值时可以省略&,而数组指针必须写&?(提示:回顾数组名的两个例外)

如果觉得有用,欢迎点赞收藏~ 

你可能感兴趣的:(c++,算法,c语言,数据结构,visual,studio)