Menu
CoddyTech

定义不透明结构体

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

现在你已经理解了不透明指针的概念,接下来看看如何完成这种模式。头文件声明某个类型存在,而源文件才是定义其实际内容的地方。

下面是完整的设置。首先,来看只有前向声明的头文件:

// counter.h
#ifndef COUNTER_H
#define COUNTER_H

typedef struct Counter Counter;  // 不完整类型

Counter *create_counter(int start);
void free_counter(Counter *c);

#endif

接下来,源文件包含实际的结构体定义:

// counter.c
#include "counter.h"
#include <stdlib.h>

struct Counter {
    int value;      // 外部无法访问!
    int step;
};

Counter *create_counter(int start) {
    Counter *c = malloc(sizeof(Counter));
    c->value = start;
    c->step = 1;
    return c;
}

void free_counter(Counter *c) {
    free(c);
}

关键点:struct Counter { ... } 只出现在 .c 文件中。任何包含 counter.h 的文件都知道 Counter 存在,但看不到 value 或 step。如果尝试在 main.c 中写入 c->value,就会导致编译错误:这些成员确实是不可见的。

这种分离是 C 中封装的基础。下一步是通过 getter 和 setter 函数提供受控访问。

challenge icon

挑战

简单

我们来构建一个 Timer 模块,演示不透明指针模式。你将练习把结构体定义隐藏在源文件中,让使用模块的代码完全看不到它的内部成员。

你将创建三个文件:

  • timer.h:这是你的公共接口。只使用前向声明来声明不透明的 Timer 类型:这里不要写结构体主体!还要声明两个函数:create_timer(接收一个表示秒数的整数,并返回一个 Timer 指针)和 free_timer(接收一个 Timer 指针并进行清理)。使用符号 TIMER_H 设置包含保护。
  • timer.c:秘密就在这个文件中。定义实际的 struct Timer,其中包含两个隐藏成员:int seconds 和 int running(运行时使用 1,停止时使用 0)。实现构造函数,为结构体分配内存,用提供的值初始化 seconds,并将 running 设为 1。实现析构函数,释放内存。
  • main.c:使用你的 Timer 模块创建一个计时器,并确认它已成功创建。由于结构体成员是隐藏的,你无法直接访问它们。这正是关键所在!只需打印一条确认创建的消息,然后释放计时器并打印一条清理消息。

你会收到一个输入:用于初始化计时器的秒数(一个整数)。

在主文件中,使用提供的秒数值创建一个计时器。打印创建消息,然后释放计时器并打印确认消息。

按以下格式打印输出:

Timer created
Timer freed

这里的关键是,main.c 可以使用 Timer 指针,但无法直接访问 seconds 或 running。如果尝试在 main.c 中写入 t->seconds,编译器就会报错,因为结构体主体只在 timer.c 中定义。这就是真正的封装:实现细节对模块的使用者完全隐藏。

亲手试试

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

int main() {
    int seconds;
    scanf("%d", &seconds);
    
    // TODO: 用提供的秒数值创建一个计时器
    
    // TODO: 打印 "Timer created"
    
    // TODO: 释放计时器
    
    // TODO: 打印 "Timer freed"
    
    return 0;
}
quiz icon自我测试

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

面向对象编程中的所有课

自己练习: C 在线编译器