C语言函数指针的用法(非常详细)
函数指针在 C语言中是一种特殊的指针类型,它用于存储函数的地址。使用函数指针可以方便地将函数作为参数传递给其他函数,实现更高级别的抽象和模块化。
由于函数指针指向函数,因此我们写一个函数作为讨论的对象,例如:
函数内部会逐个输出字符串的元素,每输出一次,计数器加 1,直到遇到 '\0'。
现在我们定义一个函数指针指向名为 print 的函数:
对函数指针进行取值后,函数指针 p 被还原为函数。我们就可以把它当作函数 print() 一样使用了,如
需要注意的是,函数的
下面是获取函数指针,并使用函数指针的示例:
一些程序员不喜欢在函数指针被取值之后使用它们,而是直接使用函数指针作为函数类型,例如:
我们可以用四个单独的函数指针变量来存储这四个函数的指针。当然,也可以把这四个函数指针组成一个数组,数组名为 funcArr。
让我们来写出这个函数指针数组的声明:
接下来将这个数组进行初始化:
最后,我们使用循环遍历这个数组,并且依次使用函数指针调用这四个函数:
当然,我们也可以更简洁一点,把函数指针直接当作函数使用,省略将函数指针转为函数的
下面是一个具体的例子,演示如何使用函数指针实现回调函数。
假设我们要实现一个通用的遍历数组的函数 traverse_and_apply,并且该函数可以对每个数组元素执行操作。用户可以提供一个自定义的操作函数,该函数接收一个整数参数并返回一个整数结果。
首先,我们需要声明一个函数指针类型,用于表示操作函数。
接下来,实现 traverse_and_apply() 函数,它接收一个整数数组、数组大小以及操作函数作为参数。
最后,在 main() 函数中调用 traverse_and_apply,分别使用 square 和 cube 作为回调函数,完整的代码如下:
由于函数指针指向函数,因此我们写一个函数作为讨论的对象,例如:
int print(char *pc) { int count = 0; while (*pc != '\0') { putchar(*pc); pc++; count++; } putchar('\n'); return count; }这个函数的参数为一个 char* 类型的指针。在 C语言中,字符串通常表示为字符数组的方式,并以空字符('\0')作为结尾。char* 是指向字符的指针,可以用来表示一个字符串的起始地址。因此,我们可以直接传入一个字符串或者字符数组作为这个函数的参数。
函数内部会逐个输出字符串的元素,每输出一次,计数器加 1,直到遇到 '\0'。
现在我们定义一个函数指针指向名为 print 的函数:
int (*p)(char *) = print;类似于数组在表达式中被转换为指向首元素的指针。当函数出现在表达式中时,将被转换为指向该函数的指针。因此,函数 print() 可以用于初始化函数指针 p。
C语言函数指针的使用
现在,函数指针 p 指向了函数 print()。我们如果想要使用指针所指向的函数,那么应该对指针进行取值,函数指针也是类似的:*p
。对函数指针进行取值后,函数指针 p 被还原为函数。我们就可以把它当作函数 print() 一样使用了,如
(*p)("HelloWorld")
。需要注意的是,函数的
()
优先级高于指针的 *
,所以需要在指针名上添加括号,先让 p 取值,还原为函数类型,再使用函数的 ()
。下面是获取函数指针,并使用函数指针的示例:
#include <stdio.h> int print(char* pc) { int count = 0; while (*pc != '\0') { putchar(*pc); pc++; count++; } putchar('\n'); return count; } int main() { int (*p)(char*) = print; int n = (*p)("HelloWorld"); printf("%d\n", n); return 0; }运行结果为:
HelloWorld
10
一些程序员不喜欢在函数指针被取值之后使用它们,而是直接使用函数指针作为函数类型,例如:
int (*p)(char *) = print; int n = p("HelloWorld"); // p被当作函数直接使用 printf("%d\n", n);C语言标准收录了这两种写法,因此 (*p)("HelloWorld") 等价于 p("HelloWorld")。很显然,p("HelloWorld") 更加方便,省略了星号和括号。
C语言函数指针数组
我们尝试定义多个函数,例如:void showStar() { printf("******\n"); } void showPlus() { printf("++++++\n"); } void showMinus() { printf("-----\n"); } void showX() { printf("XXXXXX\n"); }它们的函数被声明为
void 函数名()
,因此指向它们的指针声明如下:
- 写出这个函数的声明 void 函数名();
- 将函数名替换成指针名 void p();
- 在指针名前加 * 并用括号包括,即 void (*p)();
我们可以用四个单独的函数指针变量来存储这四个函数的指针。当然,也可以把这四个函数指针组成一个数组,数组名为 funcArr。
让我们来写出这个函数指针数组的声明:
-
funcArr 是一个数组,所以加上数组
[ ]
,方括号内填元素个数 4,即funcArr[4]
。 -
步骤 1 中的数组的元素为指针,所以加上指针的 *,即
*funcArr[4]
。指针 * 的优先级低于数组 [],所以无须括号。 -
步骤 2 中的指针指向一个函数,加上函数的 ()。这里应当先算 *,再算函数的 ()。所以需要将步骤 2 的声明先加上括号,保证先算 *,再加上函数的 (),即
(*funcArr[4])()
。如果不确定优先级,那么可以多加几个括号进行分组。 -
函数的返回值为 void,即
void (*funcArr[4])()
。
接下来将这个数组进行初始化:
void (*funcArr[4])() = {showStar, showPlus, showMinus, showX};这里我们也可以以初始化列表的形式对函数指针数组进行初始化,将函数填写在花括号里。由于函数出现在表达式中,即转换为指向该函数的指针,因此我们直接初始化数组即可。
最后,我们使用循环遍历这个数组,并且依次使用函数指针调用这四个函数:
for (int i = 0; i < 4; i++) { (*funcArr[i])(); }在这个循环中,数组下标
[]
优先级高于 *
号,funcArr[i] 获得函数指针。接着,*
将函数指针转为函数,再使用 ()
调用函数。运行结果为:
*****
++++++
------
XXXXXX
当然,我们也可以更简洁一点,把函数指针直接当作函数使用,省略将函数指针转为函数的
*
:
for (int i = 0; i < 4; i++) { funcArr[i](); }数组下标 [] 与函数调用的 () 优先级一致,即从左到右执行。funcArr[i] 获得函数指针后,直接使用 () 即可调用函数。
C语言函数指针的运用
函数指针在 C语言中有很多实际应用场景,例如实现回调函数等。回调函数是一种常见的设计模式,允许我们根据用户提供的函数来自定义算法的行为。下面是一个具体的例子,演示如何使用函数指针实现回调函数。
假设我们要实现一个通用的遍历数组的函数 traverse_and_apply,并且该函数可以对每个数组元素执行操作。用户可以提供一个自定义的操作函数,该函数接收一个整数参数并返回一个整数结果。
首先,我们需要声明一个函数指针类型,用于表示操作函数。
int (*operation_fn)(int);
接下来,实现 traverse_and_apply() 函数,它接收一个整数数组、数组大小以及操作函数作为参数。
void traverse_and_apply(int arr[], int size, int (*op)(int)) { for (int i = 0; i < size; i++) { arr[i] = op(arr[i]); } }现在,我们可以编写自定义的操作函数,例如实现元素的平方和元素的立方。
int square(int x) { return x * x; } int cube(int x) { return x * x * x; }
最后,在 main() 函数中调用 traverse_and_apply,分别使用 square 和 cube 作为回调函数,完整的代码如下:
#include <stdio.h> void traverse_and_apply(int arr[], int size, int (*op)(int)) { for (int i = 0; i < size; i++) { arr[i] = op(arr[i]); } } int square(int x) { return x * x; } int cube(int x) { return x * x * x; } int main() { int arr[] = {1, 2, 3, 4, 5}; int size = sizeof(arr) / sizeof(arr[0]); printf("初始数组为:\n"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n\n"); traverse_and_apply(arr, size, square); printf("平方计算的数组为:\n"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n\n"); traverse_and_apply(arr, size, cube); printf("立方计算的数组为:\n"); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } printf("\n"); return 0; }运行结果为:
初始数组为:
1 2 3 4 5
平方计算的数组为:
1 4 9 16 25
立方计算的数组为:
1 64 729 4096 15625