在C&C++中结构体的惯用方法

C 和 C++ 虽然都支持结构体(struct),但由于语言范式的不同(C 是过程式,C++ 是面向对象),结构体的惯用法(idioms)存在显著差异 。下面从设计哲学、语法细节、内存布局、典型用法等角度,系统对比并总结 C 与 C++ 中结构体的惯用方法

一、C 语言中结构体的惯用法

1. 核心目的:数据聚合 + 接口封装

C 没有类,结构体是组织复杂数据的主要手段,常配合函数指针模拟"对象行为"。

2. 典型惯用法

(1) 使用 typedef 简化声明
复制代码
typedef struct {
    int x, y;
} Point;

// 用法:Point p = {1, 2};

避免每次写 struct Point,提升可读性。

(2) 模拟"私有成员" + "方法"

通过不透明指针(Opaque Pointer)隐藏实现:

复制代码
// point.h
typedef struct Point Point;  // 不定义内容
Point* point_create(int x, int y);
void point_destroy(Point* p);
int point_get_x(const Point* p);

// point.c
struct Point {
    int x, y;
};
Point* point_create(int x, int y) {
    Point* p = malloc(sizeof(Point));
    p->x = x; p->y = y;
    return p;
}
// 用户无法直接访问 x/y,实现封装
(3) 柔性数组成员(FAM)处理变长数据
复制代码
typedef struct {
    size_t len;
    char data[];  // C99+
} Buffer;
Buffer* buf = malloc(sizeof(Buffer) + 100);
(4) 函数指针实现"多态"或回调
复制代码
typedef struct {
    void (*print)(const void*);
    void (*destroy)(void*);
} ObjectOps;

typedef struct {
    ObjectOps* ops;
    char name[32];
} MyObject;

Linux 内核大量使用此模式(如 file_operations

(5) 内存对齐控制(嵌入式/协议解析)
复制代码
#pragma pack(push, 1)
typedef struct {
    uint8_t type;
    uint32_t id;
} PacketHeader;  // 禁止 padding,确保 5 字节
#pragma pack(pop)

二、C++ 中结构体的惯用法

1. 核心理念:structclass,只是默认 public

在 C++ 中,structclass 唯一区别是默认访问权限struct 默认 publicclass 默认 private)。因此,struct 常用于:

  • 纯数据载体(POD / Trivial Types)
  • 需要公开成员的轻量对象
  • 模板元编程中的类型标签

2. 典型惯用法

(1) POD(Plain Old Data)结构体

用于与 C 兼容、序列化、内存映射等场景:

复制代码
1struct Vec3 {
2    float x, y, z;  // 所有成员 public,无虚函数,无自定义构造/析构
3};
4static_assert(std::is_trivial_v<Vec3>);
5static_assert(std::is_standard_layout_v<Vec3>);

可安全 memcpymemset,适合网络/文件 I/O。

(2) 聚合初始化(Aggregate Initialization)

C++11+ 支持简洁初始化:

复制代码
1struct Config {
2    int timeout;
3    bool enable_log;
4};
5
6Config cfg{30, true};        // 直接初始化
7Config cfg2{.timeout=60};    // C++20 支持 designated initializer
(3) 作为"值类型"使用(拷贝语义)

C++ 鼓励结构体具有值语义(value semantics):

复制代码
1struct Point {
2    int x = 0, y = 0;  // 默认成员初始化(C++11)
3    
4    // 编译器自动生成拷贝构造、赋值、析构(Rule of Zero)
5};
6
7Point p1{1, 2};
8Point p2 = p1;  // 安全深拷贝(若成员都是值类型)
(4) 与 STL 容器无缝配合
复制代码
1std::vector<Point> points;
2std::sort(points.begin(), points.end(), [](auto& a, auto& b) {
3    return a.x < b.x;
4});
(5) 模板中的"标签类型"或"策略"
复制代码
1struct FastMode {};
2struct SafeMode {};
3
4template<typename Mode>
5void process(Mode) {
6    if constexpr (std::is_same_v<Mode, FastMode>) {
7        // 快速路径
8    }
9}
(6) 继承与多态(虽少用,但合法)
复制代码
1struct Shape {
2    virtual double area() const = 0;
3    virtual ~Shape() = default;
4};
5
6struct Circle : Shape {
7    double r;
8    double area() const override { return 3.14 * r * r; }
9};

尽管可用,但通常用 class 表示这种"有行为"的类型

三、关键对比表

四、建议

C 语言:

  • typedef struct { ... } Name; 简化类型名。
  • 对外暴露接口时,使用不透明指针实现封装。
  • 变长数据优先考虑柔性数组而非二级指针。
  • 协议/硬件寄存器结构体务必控制对齐。

C++ 语言:

  • 纯数据结构用 struct,有复杂行为/封装用 class(风格约定)。
  • 优先遵循 Rule of Zero:避免手动管理资源(让编译器生成)。
  • 若需与 C 交互,确保结构体是 standard-layout / trivial
  • 利用 = default= delete 显式控制特殊成员函数。

五、混合开发注意事项(C/C++ 互操作)

若 C++ 代码需被 C 调用(如库开发):

复制代码
// mylib.h
#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    int id;
    float value;
} DataRecord;  // 必须是 POD

void process_record(DataRecord* r);

#ifdef __cplusplus
}
#endif

保结构体不含 C++ 特性(虚函数、引用、非 POD 成员)。

总结

  • C 的 struct 是"数据容器",靠函数指针和命名约定模拟对象。
  • C++ 的 struct 是"轻量 class",天然支持面向对象特性,但惯用于值类型。
  • 柔性数组、不透明指针、内存对齐是 C 的特色技巧;
  • 聚合初始化、Rule of Zero、POD 保证是 C++ 的现代用法。
相关推荐
静心观复1 小时前
Java 中,`1 << 1`
java·开发语言
明洞日记2 小时前
【VTK手册027】VTK 颜色连续映射:vtkColorTransferFunction 深度解析与实战指南
c++·图像处理·算法·vtk·图形渲染
Bruce_kaizy2 小时前
c++单调数据结构————单调栈,单调队列
开发语言·数据结构·c++
默凉2 小时前
win 制作vs离线安装包
c++
阿坤带你走近大数据2 小时前
Python基础知识-数据结构篇
开发语言·数据结构·python
froginwe112 小时前
AJAX 实时搜索:技术原理与实现方法
开发语言
fufu03112 小时前
Linux环境下的C语言编程(四十三)
linux·c语言·算法
发光小北2 小时前
SG-CAN (FD) NET-210(双通道 CAN (FD) 转以太网网关)特点与功能介绍
开发语言·网络·php
dragoooon342 小时前
[C++——lesson32.数据结构进阶——「初识哈希」]
数据结构·c++·哈希算法