Menu
CoddyTech

结构体中的函数指针

本课属于 Coddy C 学习旅程的“面向对象编程”单元。第 38 课,共 61 课。

到目前为止,我们一直将函数指针用作独立变量,或将它们作为参数传递。迈向多态的下一步,是将函数指针直接嵌入结构体中,让结构体拥有自己的行为。

当结构体包含函数指针时,每个实例都可以保存不同的函数。这意味着,调用函数指针时,两个相同类型的对象可以表现不同:

typedef void (*PrintFunc)(const char*);

typedef struct {
    const char* name;
    PrintFunc print;  // 作为成员的函数指针
} Printer;

Printer 结构体现在有两个成员:数据(name)和行为(print)。要使用它,你需要在初始化时将一个函数赋值给指针:

void loud_print(const char* msg) {
    printf("!!! %s !!!\n", msg);
}

int main() {
    Printer p;
    p.name = "Alert";
    p.print = loud_print;
    
    p.print("Hello");  // 输出:!!! Hello !!!
    return 0;
}

注意,我们是通过结构体调用函数的:p.print("Hello")。这看起来很像在其他语言中调用对象的方法。结构体同时包含数据和操作这些数据的函数。

这种模式是 C 语言中多态的基础:不同的实例可以被分配不同的函数,因此同一种结构体类型可以表现出不同的行为。

challenge icon

挑战

简单

让我们构建一个 Notifier 系统,演示结构体如何通过函数指针携带自己的行为。

你将创建两个文件来组织代码:

  • notifier.h:定义一个名为 NotifyFunc 的函数指针类型,它接受一个 const char* 参数且不返回任何值。然后定义一个 Notifier 结构体,其中包含一个 name(类型为 const char*)和一个类型为 NotifyFunc 的 notify 函数指针。
  • main.c:包含你的头文件,并实现两个通知函数:
    • alert_notify:按以下格式输出消息:[ALERT] message
    • info_notify:按以下格式输出消息:[INFO] message
    然后创建一个 Notifier 实例,根据输入设置相应的函数,并通过结构体的函数指针调用通知函数。

程序会接收两个输入:一个通知类型(1 表示警报,2 表示信息)和一条要显示的消息。

根据类型,将对应的函数赋给通知器的 notify 成员,然后通过结构体调用它来显示消息。

类型为 1,消息为 "System starting" 时的示例输出:

[ALERT] System starting

类型为 2,消息为 "All systems normal" 时的示例输出:

[INFO] All systems normal

记得在头文件中使用包含保护,并通过结构体成员调用函数:n.notify(message)

亲手试试

#include <stdio.h>
#include "notifier.h"

// TODO:实现 alert_notify 函数
// 应输出:[ALERT] message


// TODO:实现 info_notify 函数
// 应输出:[INFO] message


int main() {
    int type;
    char message[256];
    
    // 读取输入
    scanf("%d", &type);
    getchar(); // 读取换行符
    fgets(message, sizeof(message), stdin);
    
    // 如果 message 末尾有换行符,就将其删除
    int len = 0;
    while (message[len] != '\0') len++;
    if (len > 0 && message[len-1] == '\n') message[len-1] = '\0';
    
    // TODO:创建一个 Notifier 实例
    
    
    // TODO:根据 type(1 表示 alert,2 表示 info),
    // 将相应的函数赋给 notifier 的 notify 成员
    
    
    // TODO:通过结构体的函数指针调用通知函数
    // 使用:n.notify(message)
    
    
    return 0;
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: C 在线编译器