欢迎光临
我们一直在努力

C语言进阶|自定义类型全解:结构体/位段/枚举/联合体(思维导图+完整源码+面试考点)

前言

C 语言基础数据类型只能描述单一数据,而实际开发中经常需要组合数据、状态标记、位级存储、内存复用,这时就需要用到自定义类型。

本文全面讲解 struct 结构体、位段、enum 枚举、union 联合体四大自定义类型,搭配文本版思维导图、可运行代码、底层原理、内存规则、高频笔试题,内容适配课程设计、面试复习、工程开发,建议收藏精读。 前置说明:全文代码兼容 VS / GCC / Dev-C++,可直接复制编译。

一、自定义类型知识体系(文本思维导图)

C语言自定义类型整体框架 ├─ 基础铺垫 │  ├─ 普通内置类型:char/short/int/float/double │  ├─ 自定义类型作用:组合数据、状态枚举、位存储、内存共享 │  └─ 辅助关键字:typedef 类型重命名 ├─ 1. 结构体 struct(核心重点) │  ├─ 基本语法:声明、变量定义、匿名结构体 │  ├─ 初始化与成员访问:. 运算符、-> 指针访问 │  ├─ 结构体内存对齐(面试必考) │  │  ├─ 四大对齐规则 │  │  ├─ offsetof 偏移量计算 │  │  ├─ #pragma pack 修改默认对齐数 │  │  └─ 成员顺序对内存大小的影响 │  ├─ 结构体嵌套、结构体自引用(链表基础) │  ├─ 结构体数组、结构体指针 │  └─ 结构体传参:传值 vs 传地址(效率对比) ├─ 2. 位段(基于结构体,按bit存储) │  ├─ 语法格式:成员 : 位数 │  ├─ 内存分配规则 │  ├─ 适用场景:硬件寄存器、协议报文、标志位 │  ├─ 特性:无法取地址、不能使用scanf直接输入 │  └─ 缺点:分配规则跨平台不统一,可移植性差 ├─ 3. 枚举 enum │  ├─ 语法:枚举常量、默认赋值、手动赋值 │  ├─ 取值规则:常量自动递增 │  ├─ 优势对比 #define:类型安全、支持调试、作用域明确 │  └─ 实战场景:菜单选项、状态码、功能标识 ├─ 4. 联合体 union(共用体) │  ├─ 核心特性:所有成员共享同一块内存 │  ├─ 内存大小:等于最大成员所占字节数 │  ├─ 使用特点:同一时刻仅一个成员有效 │  ├─ 经典实战:联合体判断系统大小端 │  └─ 适用场景:内存复用、类型转换、协议解析 ├─ typedef 类型别名 │  ├─ 基础用法:给已有类型起别名 │  ├─ 结合结构体/枚举/联合体简化书写 │  └─ 易错点:区分 typedef 与 #define ├─ 高频易错 & 面试题汇总 │  ├─ 内存对齐计算、对齐数修改 │  ├─ 结构体自引用写法误区 │  ├─ 位段位数溢出问题 │  ├─ 联合体内存覆盖问题 │  └─ 四大类型使用场景区分 └─ 综合实战    ├─ 学生信息管理(结构体数组)    ├─ 简易菜单系统(枚举)    ├─ 大小端检测(联合体)    └─ 标志位存储(位段)

二、结构体 struct(使用最广泛的自定义类型)

2.1 概念与基本语法

结构体用于将多个不同类型的数据成员封装为一个整体,用来描述一个复杂对象。

2.1.1 结构体声明与变量定义

 

#include <stdio.h>

// 1. 结构体类型声明
struct Student
{
    char name[20];
    int age;
    float score;
};

// 2. 声明同时定义变量
struct Teacher
{
    char name[20];
    int id;
} t1, t2;

// 3. 匿名结构体(无类型名,只能当场使用)
struct
{
    int x;
    int y;
} point;

int main()
{
    struct Student s1;
    return 0;
}

2.2 初始化与成员访问

• 结构体普通变量:使用 . 访问成员• 结构体指针变量:使用 -> 访问成员  

#include <stdio.h>
#include <string.h>

struct Student
{
    char name[20];
    int age;
    float score;
};

int main()
{
    // 顺序初始化
    struct Student s1 = {"张三", 18, 92.5f};
    // 指定成员初始化(C99 推荐)
    struct Student s2 = {.age = 19, .name = "李四", .score = 88.0f};

    // 变量访问成员
    printf("姓名:%s 年龄:%d 分数:%.1f\\n", s1.name, s1.age, s1.score);

    // 结构体指针
    struct Student* p = &s2;
    printf("姓名:%s 年龄:%d\\n", p->name, p->age);

    // 字符数组赋值必须使用 strcpy
    strcpy(s1.name, "王五");
    printf("修改后姓名:%s\\n", s1.name);

    return 0;
}

2.3 结构体内存对齐(笔试核心考点)

对齐规则

1. 第一个成员偏移量为 0;

2. 后续成员偏移量 = 自身大小 和 默认对齐数(VS=8,GCC=4)中较小值的整数倍;

3. 结构体总大小 = 内部最大对齐数的整数倍;

4. 嵌套结构体:嵌套结构体整体按照自身最大对齐数对齐。代码验证 + 偏移量计算  

#include <stdio.h>
#include <stddef.h>   // offsetof 头文件

struct S1
{
    char c1;
    int a;
    char c2;
};

struct S2
{
    int a;
    char c1;
    char c2;
};

int main()
{
    printf("S1 总大小:%zu\\n", sizeof(struct S1));
    printf("c1 偏移:%zu\\n", offsetof(struct S1, c1));
    printf("a  偏移:%zu\\n", offsetof(struct S1, a));
    printf("c2 偏移:%zu\\n", offsetof(struct S1, c2));

    printf("\\nS2 总大小:%zu\\n", sizeof(struct S2));
    return 0;
}
修改默认对齐数
#include <stdio.h>

#pragma pack(4)   // 设置对齐数为4
struct S
{
    char c;
    int a;
};
#pragma pack()    // 恢复默认对齐

int main()
{
    printf("结构体大小:%zu\\n", sizeof(struct S));
    return 0;
}

2.4 结构体自引用(链表基础)

不能包含自身结构体变量,只能包含指向自身的指针。 #include <stdio.h>

struct Node
{
    int data;
    struct Node* next;
};

int main()
{
    struct Node n1 = {10, NULL};
    struct Node n2 = {20, &n1};

    printf("n2.data = %d\\n", n2.data);
    printf("n2.next->data = %d\\n", n2.next->data);
    return 0;
}

2.5 结构体传参

优先传地址,传值会完整拷贝结构体,效率低。

#include <stdio.h>

struct Student
{
    char name[20];
    int age;
};

// 传值(不推荐)
void PrintByVal(struct Student s)
{
    printf("姓名:%s 年龄:%d\\n", s.name, s.age);
}

// 传地址(推荐,const 保护数据)
void PrintByAddr(const struct Student* ps)
{
    printf("姓名:%s 年龄:%d\\n", ps->name, ps->age);
}

int main()
{
    struct Student s = {"小明", 17};
    PrintByVal(s);
    PrintByAddr(&s);
    return 0;
}

三、位段(结构体扩展,按位存储)

位段以比特位(bit) 为单位分配内存,极致节省空间,多用于硬件、协议开发。

• 位段成员类型:int / unsigned int / signed int• 位段没有地址,不能取地址。

#include <stdio.h>

// 位段结构体
struct BitField
{
    unsigned int a : 2;  // 占 2 bit
    unsigned int b : 3;  // 占 3 bit
    unsigned int c : 1;  // 占 1 bit
};

int main()
{
    struct BitField bf;
    bf.a = 3;
    bf.b = 7;
    bf.c = 1;

    printf("位段结构体大小:%zu 字节\\n", sizeof(struct BitField));
    printf("a=%u b=%u c=%u\\n", bf.a, bf.b, bf.c);
    return 0;
}

注意:位段内存分配规则跨平台不一致,正式可移植项目谨慎使用。

四、枚举 enum(限定取值范围)

枚举用来列出变量所有合法取值,提升代码可读性与安全性,替代宏定义。

4.1 基础用法

 

#include <stdio.h>

// 1. 默认从 0 开始递增
enum Week
{
    Mon,
    Tues,
    Wed,
    Thur,
    Fri,
    Sat,
    Sun
};

// 2. 手动赋值,后续自动递增
enum Color
{
    RED = 5,
    GREEN,
    BLUE = 10
};

int main()
{
    enum Week day = Fri;
    enum Color col = BLUE;

    printf("Fri = %d\\n", day);
    printf("BLUE = %d\\n", col);
    return 0;
}

4.2 实战:菜单状态枚举

 

#include <stdio.h>

enum Menu
{
    EXIT,
    ADD,
    SUB,
    MUL,
    DIV
};

int main()
{
    int choose = 0;
    while (1)
    {
        printf("\\n0-退出 1-加法 2-减法 3-乘法 4-除法\\n");
        scanf("%d", &choose);
        switch (choose)
        {
            case EXIT:
                printf("程序退出\\n");
                return 0;
            case ADD:
                printf("执行加法\\n");
                break;
            case SUB:
                printf("执行减法\\n");
                break;
            case MUL:
                printf("执行乘法\\n");
                break;
            case DIV:
                printf("执行除法\\n");
                break;
            default:
                printf("输入错误\\n");
                break;
        }
    }
    return 0;
}

五、联合体 union(共用内存空间)

联合体所有成员共享同一块内存,同一时刻只有一个成员有效;联合体大小等于最大成员的字节数。

5.1 基础使用

#include <stdio.h>

union Data
{
    char ch;
    int num;
    float f;
};

int main()
{
    union Data d;
    printf("联合体大小:%zu\\n", sizeof(union Data));

    d.num = 100;
    printf("num=%d ch=%d f=%f\\n", d.num, d.ch, d.f);

    d.ch = 'A';
    printf("num=%d ch=%c\\n", d.num, d.ch);

    return 0;
}

5.2 经典实战:联合体判断大小端

#include <stdio.h>

union Endian
{
    int n;
    char c;
};

int main()
{
    union Endian e;
    e.n = 1;

    if (e.c == 1)
    {
        printf("当前系统:小端字节序\\n");
    }
    else
    {
        printf("当前系统:大端字节序\\n");
    }
    return 0;
}

六、typedef 类型重命名

为已有类型起别名,简化复杂类型书写。

#include <stdio.h>

// 结构体重命名
typedef struct Student
{
    char name[20];
    int age;
} Stu;

// 枚举重命名
typedef enum Color
{
    RED,
    GREEN,
    BLUE
} Col;

int main()
{
    Stu s = {"小华", 16};
    Col c = GREEN;

    printf("姓名:%s 颜色值:%d\\n", s.name, c);
    return 0;
}

七、四大自定义类型总结与区分

1. 结构体 struct:成员独立占用内存,用于描述复合对象,内存对齐是重点。

2. 位段:基于结构体,按比特位存储,省内存,多用于硬件/协议,跨平台差。

3. 枚举 enum:限定变量合法取值,替代宏,类型安全、可读性强。

4. 联合体 union:成员共享内存,节省空间,常用于大小端判断、数据解析。

5. typedef:类型别名,简化代码,提升可维护性。

自定义类型是 C 语言从入门到进阶的分水岭,也是结构体、链表、文件、项目开发的基础,务必吃透内存规则与使用场景。

赞(0)
未经允许不得转载:171主机测评 » C语言进阶|自定义类型全解:结构体/位段/枚举/联合体(思维导图+完整源码+面试考点)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址