C语言自定义类型:结构体、内存对齐与位段

C语言自定义类型:结构体

1. 声明与初始化

1
2
3
4
5
6
7
8
struct Student
{
char name[32];
int age;
double score;
};

struct Student s = {"Lic", 20, 95.5};

也可使用指定初始化:

1
struct Student s = {.age = 20, .name = "Lic", .score = 95.5};

结构体可嵌套,也可以包含数组、指针和其他结构体成员。

2. 成员访问

对象使用 .,结构体指针使用 ->:

1
2
3
printf("%s\n", s.name);
struct Student *ps = &s;
ps->age = 21;

3. 匿名结构与typedef

1
2
3
4
5
6
7
typedef struct
{
int x;
int y;
} Point;

Point p = {10, 20};

自引用结构需要结构标签:

1
2
3
4
5
typedef struct Node
{
int value;
struct Node *next;
} Node;

4. 内存对齐

结构体成员之间可能插入填充字节,以满足平台的对齐要求并提高访问效率。

1
2
3
4
5
6
struct Example
{
char c;
int i;
double d;
};

常见规则:

  1. 首成员位于偏移0;
  2. 后续成员放在其对齐数整数倍的偏移;
  3. 结构体总大小是最大有效对齐数的整数倍;
  4. 嵌套结构体按其自身对齐要求处理。

可用 offsetof 检查成员偏移:

1
2
#include <stddef.h>
printf("%zu\n", offsetof(struct Example, i));

合理调整成员顺序有时能减少填充,但二进制协议或持久化格式不能直接依赖编译器默认布局。

5. 结构体传参

按值传递会复制整个结构体:

1
2
3
4
5
void print_student(const struct Student *student)
{
printf("%s %d %.1f\n",
student->name, student->age, student->score);
}

较大结构体通常传 const 指针,既避免复制,也明确函数不会修改对象。

6. 位段

1
2
3
4
5
6
struct Flags
{
unsigned int readable : 1;
unsigned int writable : 1;
unsigned int level : 3;
};

位段可以压缩标志数据,但布局、顺序和对齐具有实现相关性,不适合直接作为跨平台网络协议格式。

7. 易错点

  • 忘记结构体声明结尾的分号;
  • 把 . 与 -> 混用;
  • 假设结构体成员之间没有填充;
  • 用 memcmp 判断结构体逻辑相等;
  • 结构体含指针成员时只做浅拷贝。

8. 总结

结构体把多种相关数据组合成一个逻辑对象。理解成员布局、对齐和传参成本,是编写数据结构、文件格式和网络程序的重要基础。