这是一个非常核心且容易混淆的概念,因为它涉及到 C 语言的两种主要编程风格:结构化编程 和 面向对象编程。

核心结论先行
在 标准 C 语言 中,没有 public 关键字。
public 是一个访问修饰符,主要用于 面向对象编程,例如在 C++、Java、C# 等语言中,用来控制类成员(变量和方法)的可见性,表示它们是“公有的”,可以在任何地方被访问。
C 语言本身不是一门纯粹的面向对象语言,它没有类、继承等内置的 OOP 特性,C 语言可以通过一些特殊的技巧和约定来模拟出类似 public、private 的访问控制,尤其是在开发大型库或框架时。
下面我们分两部分来详细解释:

第一部分:为什么标准 C 没有 public?
C 语言的设计哲学是简洁、高效、贴近硬件,它通过 struct(结构体)来组织数据,通过函数来操作数据,这种风格被称为结构化编程 或 过程化编程。
在 C 语言中,一个 struct 默认情况下,其所有成员都是“公开”的,你可以直接创建一个结构体变量,然后通过点号 访问它的任何一个成员。
示例:标准的 C 结构体
#include <stdio.h>
#include <string.h>
// 定义一个“学生”结构体
struct Student {
int id; // 学号
char name[50]; // 姓名
float score; // 分数
};
int main() {
// 创建一个学生变量
struct Student stu1;
// 直接访问和修改结构体的成员(这就是“公开”的体现)
stu1.id = 101;
strcpy(stu1.name, "张三");
stu1.score = 95.5f;
// 直接读取成员
printf("学生ID: %d\n", stu1.id);
printf("学生姓名: %s\n", stu1.name);
printf("学生分数: %.2f\n", stu1.score);
return 0;
}
在上面的代码中,Student 结构体的所有成员 id, name, score 都可以被外部代码直接访问和修改,这就是 C 语言默认的行为,它没有 public 关键字来声明这些成员是“公有的”,因为它们天生就是公有的。

第二部分:如何在 C 中模拟 public 和 private?
虽然 C 语言没有 public,但在实际开发中,我们常常希望隐藏一个结构体的内部实现细节,只暴露必要的接口,以防止外部代码误操作,这被称为信息隐藏 或 数据封装,这是面向对象编程中的一个重要原则。
模拟 public 和 private 的常用方法是 不透明指针 或 句柄。
模拟方法:不透明指针
这种方法的核心思想是:
private部分:将结构体的真正定义放在一个.c文件中(源文件),而不是.h文件(头文件)。public部分:在头文件中,只声明一个指向该结构体的指针(struct MyStruct*),不透露其内部成员。public接口:提供一组公共的函数(以mystruct_开头,作为命名空间),这些函数接收不透明指针作为参数,并在.c文件内部实现对结构体成员的操作。
这样,外部代码只能通过你提供的公共函数来操作数据,而无法直接访问和修改结构体内部,从而实现了封装。
示例:模拟封装的“学生”管理
student.h (公共接口头文件)
这个文件是给用户(其他程序员)看的,它只告诉用户“有什么功能可以用”,但隐藏了“这些功能是如何实现的”。
// student.h #ifndef STUDENT_H #define STUDENT_H // 1. 前向声明,告诉编译器 "Student" 是一个结构体类型 // 但不透露它的内部成员,这就是 "private" 的核心。 struct Student; // 2. 公共的构造函数 (模拟 public) // 返回一个不透明指针,用户只能持有这个指针,但看不到内部。 struct Student* student_create(int id, const char* name, float score); // 3. 公共的访问方法 (模拟 public) // 用户只能通过这些函数来获取信息。 int student_get_id(const struct Student* s); const char* student_get_name(const struct Student* s); float student_get_score(const struct Student* s); // 4. 公共的修改方法 (模拟 public) // 用户只能通过这些函数来修改信息。 void student_set_score(struct Student* s, float new_score); // 5. 公共的析构函数 (模拟 public) void student_destroy(struct Student* s); #endif // STUDENT_H
student.c (私有实现文件)
这个文件包含了结构体的真正定义和所有函数的具体实现,这是“私有的”部分。
// student.c
#include "student.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 6. 在这里定义结构体的真正成员 (private)
// 这个定义对外是不可见的。
struct Student {
int id;
char name[50];
float score;
};
// 7. 实现公共函数
struct Student* student_create(int id, const char* name, float score) {
struct Student* s = (struct Student*)malloc(sizeof(struct Student));
if (s) {
s->id = id;
strncpy(s->name, name, sizeof(s->name) - 1);
s->name[sizeof(s->name) - 1] = '\0'; // 确保字符串终止
s->score = score;
}
return s;
}
int student_get_id(const struct Student* s) {
return s->id;
}
const char* student_get_name(const struct Student* s) {
return s->name;
}
float student_get_score(const struct Student* s) {
return s->score;
}
void student_set_score(struct Student* s, float new_score) {
if (s) {
s->score = new_score;
}
}
void student_destroy(struct Student* s) {
if (s) {
free(s);
}
}
main.c (使用代码)
用户(其他程序员)只需要包含 student.h,并使用提供的公共函数即可,他无法直接操作 stu->id 或 stu->score。
// main.c
#include "student.h"
#include <stdio.h>
int main() {
// 用户只能持有不透明指针,无法知道 Student 内部有什么
struct Student* stu = student_create(101, "李四", 88.0f);
// 只能通过公共函数访问数据
printf("学生姓名: %s\n", student_get_name(stu));
printf("学生分数: %.2f\n", student_get_score(stu));
// 只能通过公共函数修改数据
student_set_score(stu, 92.5f);
printf("更新后分数: %.2f\n", student_get_score(stu));
// 只能通过公共函数销毁对象
student_destroy(stu);
return 0;
}
总结这种模拟方法:
public:指student.h中声明的所有函数,以及不透明指针struct Student*本身(用户可以传递它)。private:指student.c中struct Student的具体定义,包括它的所有成员变量。
通过这种方式,C 语言实现了与 OOP 中 public 和 private 类似的效果,极大地提高了代码的模块化程度和可维护性。
第三部分:C++ 中的 public
为了形成对比,我们简单看一下在 C++ 中 public 是如何工作的,C++ 是 C 的超集,它原生支持面向对象编程。
在 C++ 中,public 是一个访问控制说明符,用于类内部。
示例:C++ 中的 public
#include <iostream>
#include <string>
class Student { // C++ 使用 class 关键字
private: // private 成员只能在类的内部访问
int id;
std::string name;
float score;
public: // public 成员可以在类的内部和外部访问
// 构造函数
Student(int i, const std::string& n, float s) : id(i), name(n), score(s) {}
// 公共方法
int getId() const { return id; }
std::string getName() const { return name; }
float getScore() const { return score; }
void setScore(float newScore) { score = newScore; }
};
int main() {
Student stu(101, "王五", 75.0f);
// 可以通过公共方法访问数据
std::cout << "学生姓名: " << stu.getName() << std::endl;
// 不能直接访问私有成员,下面的代码会编译错误
// stu.id = 102; // Error: 'int Student::id' is private
return 0;
}
| 特性 | 标准 C 语言 | C++ 语言 |
|---|---|---|
public 关键字 |
没有 | 有,是访问修饰符 |
| 封装实现方式 | 通过 不透明指针 和 函数接口 手动模拟 | 语言内置的 public, private, protected 关键字 |
| 数据结构 | struct(默认成员公开) |
struct(默认成员公开)和 class(默认成员私有) |
| 核心思想 | 结构化/过程化编程 | 面向对象编程 |
当你在 C 语言的语境下看到“public”这个词时,可以确定它指的是一种编程技巧和设计模式,而不是语言本身的关键字,它的目的是为了在 C 的框架内实现数据封装和信息隐藏,以达到更好的代码组织。
