在C语言中,指针函数和函数指针是两个极易混淆的概念。它们的名字只是词序不同,但含义截然相反。下面我将从定义、用法、典型场景到复杂声明,为你彻底厘清这两个概念。
一、指针函数 —— 返回指针的函数
1. 概念
指针函数本质上是一个函数,它的返回值类型是一个指针(也就是一个地址)。
2. 定义格式
int *func(int a, int b); // func 是函数,返回 int* 类型的指针解读:func先和(int a, int b)结合,说明它是一个函数,函数的返回值是int *类型。
3. 典型例子
返回一个静态变量的地址
char *get_weekday(int n) { static char *weekdays[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; if (n < 1 || n > 7) return NULL; return weekdays[n - 1]; } char *day = get_weekday(3); // day 指向 "Wednesday"注意这里使用了static修饰数组,因为如果返回局部非静态数组的地址,函数退出后该内存会被回收,返回的指针就是“野指针”。
返回动态分配的内存
int *create_array(int size) { int *p = (int *)malloc(size * sizeof(int)); return p; // 返回堆内存的指针 }这是合法的,因为堆内存的生命周期由程序员手动控制。但调用方必须记得free。
4. 注意事项
绝不能返回局部非静态变量的地址。
返回的指针在使用前需检查是否为空(如
malloc失败)。返回指针的函数常用于分配资源、查找子串、返回结构体指针等场景。
二、函数指针 —— 指向函数的指针
1. 概念
函数指针是一个指针变量,它存储的是函数的入口地址。通过它可以间接调用函数,如同通过数据指针间接访问变量一样。
2. 定义格式
int (*p)(int, int); // p 是指针,指向 返回值为int、参数为(int, int) 的函数解读:因为括号的优先级,*p先结合,表明p是一个指针;然后外面是函数的调用操作符(int, int),表明它指向一个函数;函数返回int。
若不写括号int *p(int, int)则声明的是一个指针函数(返回int*的函数),所以括号非常关键。
3. 初始化与调用
int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int (*pfunc)(int, int) = &add; // 初始化,取地址符可以省略,等价于 = add int result = pfunc(3, 4); // 通过指针调用,等价于 (*pfunc)(3, 4)4. 主要用途
(1) 回调函数
将函数作为参数传给另一个函数,实现灵活的定制行为。标准库的qsort就是典型例子:
#include <stdlib.h> int cmp_int(const void *a, const void *b) { return (*(int *)a - *(int *)b); } int arr[] = {5, 2, 8, 1, 9}; qsort(arr, 5, sizeof(int), cmp_int); // cmp_int 就是一个函数指针实参(2) 函数指针数组
将一系列函数按序号放入数组,实现类似“跳转表”的功能,常用于菜单驱动、有限状态机等。
void func0() { printf("Menu 0\n"); } void func1() { printf("Menu 1\n"); } void func2() { printf("Menu 2\n"); } void (*menu[])(void) = {func0, func1, func2}; int choice = 1; if (choice >= 0 && choice < 3) menu[choice](); // 调用 func1(3) 结构体中的函数指针
可在结构体中封装“行为”,模拟简易的面向对象。
struct Calculator { int (*op)(int, int); }; int multiply(int a, int b) { return a * b; } struct Calculator calc; calc.op = multiply; int product = calc.op(6, 7); // 425. 复杂声明与typedef简化
函数指针的声明如果嵌套多层会非常难读,例如:
void (*(*pf)(int, void (*)(int)))(int);这种时候强烈推荐用typedef分步定义:
typedef void (*Callback)(int); // 函数指针类型:参数int,返回void typedef Callback (*ComplexFunc)(int, Callback); // 返回 Callback 的函数指针 ComplexFunc pf; // 这样清晰多了三、核心区别对比
| 项目 | 指针函数 | 函数指针 |
|---|---|---|
| 本质 | 函数,返回值为指针 | 变量,存储函数地址 |
| 声明示例 | int *f(int); | int (*f)(int); |
| 用法 | 调用后得到一个地址 | 通过它调用所指的函数 |
| 常见场景 | 动态内存分配、返回字符串数组 | 回调、策略模式、跳转表 |
| 内存占用 | 函数体代码 + 返回值指针 | 只占一个指针大小的内存(4/8 字节) |
| 语法中的括号 | 不需要额外括号 | 必须用括号将*和指针名括起来 |
四、一张 Mermaid 脑图帮你记住
五、小结
指针函数的核心是“函数”,它返回一个地址;
函数指针的核心是“指针”,它指向一个函数。
书写时牢记括号的优先级:
int (*p)()表示p是指针,int *p()表示p是函数返回指针。熟练掌握函数指针是理解回调机制、编写灵活库和阅读Linux内核源码等进阶内容的基础。