首页 > 编程笔记 > C语言笔记 阅读:45

C语言函数指针的用法(非常详细)

函数指针在 C语言中是一种特殊的指针类型,它用于存储函数的地址。使用函数指针可以方便地将函数作为参数传递给其他函数,实现更高级别的抽象和模块化。

由于函数指针指向函数,因此我们写一个函数作为讨论的对象,例如:
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 函数名(),因此指向它们的指针声明如下:
我们可以用四个单独的函数指针变量来存储这四个函数的指针。当然,也可以把这四个函数指针组成一个数组,数组名为 funcArr。

让我们来写出这个函数指针数组的声明:
  1. funcArr 是一个数组,所以加上数组 [ ],方括号内填元素个数 4,即 funcArr[4]
  2. 步骤 1 中的数组的元素为指针,所以加上指针的 *,即 *funcArr[4]。指针 * 的优先级低于数组 [],所以无须括号。
  3. 步骤 2 中的指针指向一个函数,加上函数的 ()。这里应当先算 *,再算函数的 ()。所以需要将步骤 2 的声明先加上括号,保证先算 *,再加上函数的 (),即 (*funcArr[4])()。如果不确定优先级,那么可以多加几个括号进行分组。
  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

这个例子展示了使用函数指针实现回调函数。traverse_and_apply() 函数可以根据用户提供的操作函数(如 square 或 cube)对数组元素执行不同的操作。这种设计模式增加了代码的灵活性和可复用性。

相关文章