C 语言结构体

内容简介

struct 用于把多个相关数据组合成一个自定义类型。C 语言没有类、构造函数、析构函数、成员函数和访问控制,结构体主要负责保存数据;行为通常由普通函数完成。

本篇重点讲 C 结构体的用法、内存布局、和 C++ struct 的区别。联合体见 C语言联合体,位域见 C语言位域

用法

基本定义

typedef struct Student {
    char name[32];
    int age;
    double score;
} Student;
 
Student stu = {"Alice", 18, 95.5};

结构体对象包含所有成员,每个成员都有自己的存储空间。

成员访问

Student stu = {"Bob", 20, 88.0};
Student *p = &stu;
 
printf("%d\n", stu.age);
printf("%d\n", p->age);   // 等价于 (*p).age

指定初始化

Student a = {"Alice", 18, 95.5};
Student b = {.age = 20, .name = "Bob", .score = 88.0};

指定初始化器适合成员较多、成员顺序可能调整的结构体。

结构体和函数组织模块

typedef struct Counter {
    int value;
} Counter;
 
void counter_init(Counter *counter)
{
    if (counter != NULL) {
        counter->value = 0;
    }
}
 
void counter_add(Counter *counter, int delta)
{
    if (counter != NULL) {
        counter->value += delta;
    }
}

C 中常用“结构体保存状态,普通函数操作状态”的方式组织模块。

内存对齐和填充

typedef struct Example {
    char a;
    int b;
    char c;
} Example;

sizeof(Example) 通常大于 sizeof(char) + sizeof(int) + sizeof(char),因为编译器会插入填充字节满足对齐要求。

减少填充的常见方法是按成员对齐需求从大到小排列:

typedef struct Better {
    int b;
    char a;
    char c;
} Better;

offsetof

#include <stddef.h>
 
size_t offset = offsetof(Student, score);

offsetof 用于获取结构体成员偏移,常见于底层容器、序列化和系统接口。

C struct 和 C++ struct 的区别

对比点C 语言 structC++ struct
成员函数不支持支持
构造/析构函数不支持支持
访问控制不支持,成员都可直接访问支持 public / private / protected,默认 public
继承不支持支持继承
多态不支持虚函数支持虚函数和运行期多态
默认初始化依赖初始化器或手动赋值可写默认成员初始化、构造函数
类型名使用typedef 时要写 struct Student可直接写 Student
设计定位数据聚合和 C 风格模块class 几乎同等能力,常用于简单公开数据对象

C 示例:

struct Student {
    int age;
};
 
struct Student stu;  // C 中未 typedef 时要带 struct

C++ 示例:

struct Point {
    int x = 0;
    int y = 0;
 
    void move(int dx, int dy)
    {
        x += dx;
        y += dy;
    }
};
 
Point p;
p.move(1, 2);

关键理解:C 的结构体只是数据布局工具;C++ 的 struct 是一种默认 public 的类。

最佳代码实践

  • 把强相关的数据放进结构体,例如学生的姓名、年龄、成绩。
  • C 中常用 xxx_initxxx_destroyxxx_print 这类普通函数管理结构体。
  • 对外接口优先传 const T * 表示只读,传 T * 表示可能修改。
  • 对外暴露不完整类型可以隐藏内部布局,模拟封装。
  • 需要跨平台存储或网络传输时,不要直接把结构体内存写入文件或网络。

错误用法

typedef struct User {
    char *name;
} User;
 
User user;
strcpy(user.name, "Alice");  // 错误:name 没有指向有效缓冲区
Student stu;
printf("%d\n", stu.age);     // 错误:stu 未初始化,age 值不确定
fwrite(&stu, sizeof(stu), 1, fp);  // 错误倾向:填充字节、端序、对齐都不可移植

注意事项

  • 结构体可以整体赋值,但如果成员里有指针,复制的是地址,不是指向内容的深拷贝。
  • 结构体末尾可以使用柔性数组成员,常见于变长对象,但需要配合动态分配。
  • #pragma pack 或编译器打包属性会改变对齐,可能降低性能或导致某些平台访问异常。
  • C++ 中如果结构体开始承担复杂行为和不变量,通常应考虑 class 和封装边界。

相关:CandCpp结构体C语言联合体C语言位域C语言存储类型CandCpp底层知识

学习路径