[从0开始的C语言] C语言结构体详解

目录

1. 结构体基础入门

1.1 为什么需要结构体?

1.2 结构体类型定义

2. 结构体变量操作

2.1 变量声明方式

2.2 变量初始化

3. 结构体的内存对齐

3.1 为什么需要内存对齐?

3.2 内存对齐规则

3.2.1 内存对齐规则详解

3.3 优化技巧

3.4 查看与修改对齐

4. 联合体(Union)

4.1 定义联合体

4.2 联合体的内存布局

4.3 用 union 判断字节序

5. 枚举(Enum)

6. 总结

在C语言中,结构体(struct)是一种自定义的数据类型,可以将多个不同类型的数据组合在一起。本文将介绍结构体的基本概念、变量创建与初始化,并重点解析结构体的内存对齐。此外,我们还将介绍与结构体相关的union(联合体)和enum(枚举)。

1. 结构体基础入门

struct 结构体标签 {
    类型 成员1;
    类型 成员2;
    // ...
};

1.1 为什么需要结构体?

当我们要描述一个复杂对象时(如学生信息),单一数据类型无法满足需求。结构体可以将不同类型的数据打包成一个整体

// 传统方式需要多个独立变量
int stu_id;
char stu_name[20];
float stu_score;

// 结构体解决方案
struct Student {
    int id;
    char name[20];
    float score;
};

1.2 结构体类型定义

使用struct关键字创建自定义类型:

struct 结构体标签 {
    类型 成员1;
    类型 成员2;
    // ...
};

示例:通讯录联系人

struct Contact {
    char name[30];
    char phone[12];
    int age;
    char address[50];
};

2. 结构体变量操作

2.1 变量声明方式

// 方式1:声明时直接创建
struct Student {
    // 成员列表
} stu1, stu2;

// 方式2:先定义类型再声明变量
struct Student stu3;

// 方式3:使用typedef简化
typedef struct {
    // 成员列表
} Student;
Student stu4;

2.2 变量初始化

// 顺序初始化
struct Point {
    int x;
    int y;
};
struct Point p1 = {10, 20};

// C99指定初始化(可乱序)
struct Point p2 = {.y = 30, .x = 5};

// 结构体数组初始化
struct Student class[3] = {
    {101, "Alice", 89.5},
    {102, "Bob", 92.0},
    {103, "Charlie", 78.5}
};

3. 结构体的内存对齐

C语言的结构体在内存中存储时,会遵循内存对齐规则,以提高CPU的访问效率。

3.1 为什么需要内存对齐?

  • 硬件访问效率:CPU读取内存时以4/8字节为单位

  • 平台移植性:不同系统可能有不同的对齐要求

3.2 内存对齐规则

通过实例分析内存布局:

struct Example {
    char a;     // 1字节
    int b;      // 4字节
    char c;     // 1字节
};

未对齐状态猜想:

[char][   int   ][char] → 1+4+1=6字节

实际内存布局(假设4字节对齐):

Offset 0: [char a][填充3字节]
Offset 4: [    int b    ]
Offset 8: [char c][填充3字节]
总大小:12字节

3.2.1 内存对齐规则详解

  1. 对齐原则

    • 每个成员变量的偏移量必须是该成员**自身类型大小或默认对齐数(通常是编译器默认的对齐方式)的整数倍。

    • 结构体的总大小必须是最大对齐数的整数倍

  2. 示例解析

    • char a; 占 1 字节,但 int b; 需要4字节对齐,因此 a 后面会填充 3 个字节。

    • int b; 按 4 字节对齐,占 4 字节。

    • char c; 需要 1 字节对齐,占 1 字节。

    • 结构体总大小需要是最大对齐数 4 的整数倍,因此最终大小是 12 字节。

0   1   2   3   4   5   6   7   8   9   10  11
+---+---+---+---+---+---+---+---+---+---+---+---+
| a |   empty   |       b       | c |   empty   |
+---+---+---+---+---+---+---+---+---+---+---+---+

最终结构体大小为 12 字节,而不是 6 字节。

3.3 优化技巧

调整成员顺序可节省空间:

// 优化前:12字节
struct S1 {
    char a;
    int b;
    char c;
};

// 优化后:8字节
struct S2 {
    int b;
    char a;
    char c;
};

优化前:

0   1   2   3   4   5   6   7   8   9   10  11
+---+---+---+---+---+---+---+---+---+---+---+---+
| a |   empty   |       b       | c |   empty   |
+---+---+---+---+---+---+---+---+---+---+---+---+

优化后:

0   1   2   3   4   5   6   7   8
+---+---+---+---+---+---+---+---+
|       b       | a | c | empty |  
+---+---+---+---+---+---+---+---+

3.4 查看与修改对齐

查看结构体大小

printf("结构体大小:%zu\n", sizeof(struct Example));

修改对齐方式

#pragma pack(1) // 设置1字节对齐
struct PackedStruct {
    // 成员定义
};
#pragma pack()  // 恢复默认对齐

offsetof宏查看成员偏移量

// 查看成员偏移示例
#include <stddef.h>
printf("b的偏移量:%zu\n", offsetof(struct Example, b));

4. 联合体(Union)

union(联合体)与struct类似,但所有成员共享同一块内存,因此联合体的大小等于其最大成员的大小。

4.1 定义联合体

union Data {
    int i;
    float f;
    char str[20];
};

4.2 联合体的内存布局

0   1   2   3   4   5   ...     19  
+---+---+---+---+---+---+---+---+
|           str (20B)           |
+---+---+---+---+---+---+---+---+

整个 union 的大小是 20 字节,因为 str[20] 是最大成员。

4.3 用 union 判断字节序

#include <stdio.h>

union EndianTest {
    int i;
    char c;
};

int main() {
    union EndianTest test;
    test.i = 1;
    if (test.c == 1) {
        printf("小端序\n");
    } else {
        printf("大端序\n");
    }
    return 0;
}

如果输出 小端序,说明最低字节存储在最低地址(即 Little Endian 方式)。

5. 枚举(Enum)

enum(枚举)用于定义一组具名整数常量,提升代码可读性。

定义枚举类型:

enum Color {
    RED,    // 默认值 0
    GREEN,  // 默认值 1
    BLUE    // 默认值 2
};

可以手动指定值:

enum Color {
    RED = 1,
    GREEN = 5,
    BLUE = 10
};

使用枚举:

enum Color c = GREEN;
printf("Color: %d\n", c); // 输出 5

6. 总结

  • struct 组合不同类型数据,支持内存对齐。

  • union 共享同一块内存,仅存储一个成员的值,节省内存但有数据覆盖风险。

  • enum 定义具名整数常量,提高可读性。

  • 结构体优化成员顺序可以减少内存浪费。

  • 使用 union 可以判断系统的字节序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值