C语言自定义类型:结构体完全指南

C语言自定义类型:结构体完全指南

结构体是C语言中最重要的自定义类型,允许将不同类型的数据组合成一个整体。本文将系统讲解结构体的声明、初始化、成员访问、内存对齐规则、传参方式以及位段的应用,帮助读者掌握结构体的核心用法与底层细节。

目录


一、结构体类型的声明

结构体是一组值的集合,每个成员可以是不同类型。

c 复制代码
struct Stu {
    char name[20];
    int age;
    char sex[5];
    char id[20];
};  // 分号不能丢

1.1 特殊的声明(匿名结构体)

声明时可以省略结构体标签(tag),但此类结构体只能使用一次:

c 复制代码
struct {
    int a;
    char b;
    float c;
} x;  // 直接定义变量

注意:两个匿名结构体即使成员相同,也被视为不同类型,不能相互赋值。

1.2 结构的自引用

在链表的节点中,需要包含指向自身类型的指针:

c 复制代码
struct Node {
    int data;
    struct Node* next;  // 正确,指针大小固定
};

错误示例struct Node next; 会导致无限递归,无法计算大小。

使用 typedef 时需注意:

c 复制代码
typedef struct Node {
    int data;
    struct Node* next;  // 必须用 struct Node,不能用 Node
} Node;

二、结构体变量的创建和初始化

c 复制代码
struct Stu s1 = {"张三", 20, "男", "20230818001"};  // 顺序初始化
struct Stu s2 = {.age = 18, .name = "李四", .id = "20230818002", .sex = "女"};  // 指定顺序

三、结构成员访问操作符

  • 点操作符 . :结构体变量直接访问成员,如 s1.age
  • 箭头操作符 -> :结构体指针访问成员,如 ps->age

四、结构体内存对齐

这是计算结构体大小的核心考点。

4.1 对齐规则

  1. 第一个成员:放在偏移量为0的地址处。
  2. 其他成员 :对齐到对齐数 的整数倍地址。
    对齐数 = min(编译器默认对齐数, 成员大小)
    VS默认对齐数为8,Linux默认为4。
  3. 结构体总大小 :必须是最大对齐数的整数倍。
  4. 嵌套结构体:嵌套的结构体对齐到自身最大对齐数的整数倍,整体大小是所有最大对齐数的整数倍。

4.2 示例分析

c 复制代码
struct S1 {
    char c1;  // 1字节,偏移0
    int i;    // 4字节,对齐数4,偏移从4开始
    char c2;  // 1字节,对齐数1,偏移8
};  // 最大对齐数4,总大小须为4的倍数 → 12字节

struct S2 {
    char c1;  // 偏移0
    char c2;  // 偏移1
    int i;    // 对齐数4,偏移从4开始
};  // 总大小8字节

节省空间技巧:将占用小的成员集中在一起。

4.3 修改默认对齐数

使用 #pragma pack(1) 设置对齐数为1,可取消对齐(紧凑存储)。

c 复制代码
#pragma pack(1)
struct S { char c1; int i; char c2; };
#pragma pack()
// sizeof(struct S) = 1+4+1 = 6

五、结构体传参

c 复制代码
void print1(struct S s) { ... }   // 传值:拷贝整个结构体,效率低
void print2(struct S* ps) { ... } // 传址:只传指针,效率高

结论:结构体传参应优先传递指针(地址),避免压栈开销。


六、结构体实现位段

6.1 什么是位段

位段允许指定成员占用的比特位数 ,用于节省内存。

声明规则:成员类型为 intunsigned intsigned intchar,成员名后跟冒号和数字。

c 复制代码
struct A {
    int _a : 2;   // 占2 bit
    int _b : 5;   // 占5 bit
    int _c : 10;
    int _d : 30;
};

sizeof(struct A) 在VS中通常为8字节(因4+4字节,成员共47bit,需两个int)。

6.2 位段的内存分配

  • 位段按需以 int(4字节)或 char(1字节)为单位开辟空间。
  • 位段不跨平台,各编译器实现可能不同(如顺序、舍弃规则未定义)。

示例(VS2013下):

c 复制代码
struct S {
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
};
struct S s = {0};
s.a = 10;  // 10的二进制1010,但只取低3位 → 010
s.b = 12;  // 1100
s.c = 3;   // 00011
s.d = 4;   // 0100

内存布局(从左到右分配,低地址在右)需依编译器实际测试。

6.3 位段的跨平台问题

  • int 位段被当作有符号还是无符号不确定。
  • 最大位数不确定(16位机器最大16)。
  • 成员分配方向(从左向右或从右向左)未定义。
  • 剩余位是否利用不确定。

建议:位段适用于对内存要求极高的场景(如网络协议),但可移植程序中应避免。

6.4 位段的应用

IP数据报头部等网络协议字段常使用位段表示,以节省带宽。

6.5 位段使用的注意事项

不能对位段成员取地址 (因为其可能跨越字节边界)。无法用 scanf 直接输入,需借助临时变量:

c 复制代码
int b;
scanf("%d", &b);
sa._b = b;

总结:结构体是组织不同类型数据的容器,其内存对齐规则保证了访问效率,但可能产生空间浪费,可通过调整成员顺序或修改对齐数优化。位段以bit为单位分配内存,适合表示标志字段,但存在跨平台问题。结构体传参应使用指针以提升性能。掌握结构体是学习链表、树等数据结构的基础。

相关推荐
方也_arkling1 小时前
【Java-Day19】集合3 List中常见的方法和5种遍历方式
java·开发语言
AI玫瑰助手1 小时前
Python函数:局部变量与全局变量的作用域
开发语言·python·信息可视化
字节高级特工1 小时前
C++11(二) 革新:引用折叠与lambda表达式
java·开发语言·c++·算法
萨小耶1 小时前
[Java学习日记11】聊聊深拷贝和浅拷贝
java·开发语言·学习
xiaoshuaishuai81 小时前
C# AvaloniaUI‌的IValueConverter
开发语言·c#
白驹笙鸣2 小时前
STL allocator作用
开发语言·c++
小小编程路2 小时前
C++ STL 原理与性能
开发语言·c++
码不停蹄的玄黓2 小时前
Java线程池生命周期
java·开发语言
社交怪人2 小时前
【适合晨练】信息学奥赛一本通C语言解法(题号2054)
c语言