☰
C语言里的“反义词兄弟”:指针函数 vs 函数指针
2026/10/13 5:48:49 网站建设 项目流程

在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); // 42

5. 复杂声明与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内核源码等进阶内容的基础。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询