C语言自定义类型:结构体(struct)、联合体(union)和枚举类型(enum)详解

目录

一、自定义类型概述

二、结构体(struct)

(一)定义与用途

(二)初始化

(三)访问成员

(四)内存布局

三、联合体(union)

(一)定义与用途

(二)初始化

(三)访问成员

(四)内存布局

四、枚举类型(enum)

(一)定义与用途

(二)初始化

(三)访问成员

(四)内存布局

五、内存对齐

(一)内存对齐的概念

(二)默认对齐规则

(三)改变对齐规则

六、总结


一、自定义类型概述

在C语言中,自定义类型是程序员根据实际需求定义的新型数据结构,包括结构体(struct)、联合体(union)和枚举类型(enum)。这些类型能够帮助程序员更好地组织和管理数据,使代码更具可读性和可维护性。理解这些自定义类型及其相关的内存对齐等知识点,对于掌握C语言的高级应用至关重要。

二、结构体(struct

(一)定义与用途

结构体是一种用户自定义的数据类型,它允许将多个不同类型的数据组合成一个逻辑单元。结构体的定义格式如下:

struct 结构体名
{
    数据类型1 成员1;
    数据类型2 成员2;
    ...
    数据类型n 成员n;
};

示例:

struct Student
{
    int id;
    char name[50];
    float score;
};

结构体常用于表示具有多种属性的实体,如学生信息、员工信息等。

(二)初始化

结构体可以通过以下几种方式初始化:

逐个成员初始化

struct Student s1;
s1.id = 101;
s1.name = "Alice";
s1.score = 90.5;

使用大括号初始化

struct Student s2 = {102, "Bob", 85.0};

如果省略某些成员的初始化值,它们会被自动初始化为0或空字符串。

(三)访问成员

结构体的成员可以通过点运算符(.)访问和成员访问符(->)。例如:

printf("ID: %d, Name: %s, Score: %.2f\n", s1.id, s1.name, s1.score);
struct Student* ps = &s1;
printf("ID: %d, Name: %s, Score: %.2f\n", ps->id, ps->name, ps->score);

(四)内存布局

结构体的内存布局遵循一定的规则:

成员按声明顺序存储:结构体的成员在内存中按照声明的顺序依次存储。

内存对齐:为了提高访问效率,结构体的成员通常会按照一定的对齐规则存储。对齐规则与编译器的设置(如#pragma pack)和目标平台的硬件特性有关。默认情况下,结构体的对齐规则是:

1. 第一个成员在与结构体变量偏移量为0的地址处

2. 每个成员的起始地址必须是其自身大小的倍数

3. 结构体的总大小必须是其最大成员大小的倍数

示例:

struct Example
{
    char a;       // 1字节   0
    int b;        // 4字节   4-7
    short c;      // 2字节   8-9  0-9总共10字节,填充至4的倍数,最终12字节
};

在默认对齐规则下,Example的内存布局如下:

a占用1字节,然后有3字节的填充。

b从第4字节开始,占用4字节。

c从第8字节开始,占用2字节。

结构体总大小为12字节。

三、联合体(union

(一)定义与用途

联合体是一种特殊的数据类型,它允许不同的数据类型共享同一块内存。联合体的定义格式如下:

union 联合体名
{
    数据类型1 成员1;
    数据类型2 成员2;
    ...
    数据类型n 成员n;
};

示例:

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

联合体常用于节省内存空间,或者实现多种数据类型的动态切换。

(二)初始化

联合体的初始化只能对第一个成员进行初始化。

示例:

union Data d = {100};  // 初始化第一个成员i

如果需要初始化其他成员,需要在运行时手动赋值。

(三)访问成员

联合体的成员可以通过点运算符(.)访问,但需要注意的是,所有成员共享同一块内存。因此,修改一个成员会影响其他成员的值。

示例:

d.i = 100;
printf("d.i = %d\n", d.i);  // 输出100
printf("d.f = %f\n", d.f);  // 输出无意义的浮点数

(四)内存布局

联合体的内存布局特点如下:

所有成员共享同一块内存:联合体的大小最小是其最大成员的大小,并且最终要对齐到最大成员对对齐数的整数倍。

成员的地址相同:联合体的所有成员的起始地址相同。

示例:

union Data
{
    int i;        // 4字节
    float f;      // 4字节
    char str[8];  // 8字节
};

在默认情况下,Data的大小为8字节(最大成员str的大小),所有成员共享这8字节的内存。

四、枚举类型(enum

(一)定义与用途

枚举类型是一种用户自定义的整数类型,它允许为整数值赋予有意义的标识符。枚举类型的定义格式如下:

enum 枚举类型名
{
    枚举值1 = 值1,
    枚举值2 = 值2,
    ...
    枚举值n = 值n
};

示例:

enum Weekday
{
    MONDAY = 1,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
};

枚举类型常用于表示一组有限的、具有明确含义的整数值,如星期几、状态码等。枚举类型成员变量的值默认从0开始,依次递增。

(二)初始化

枚举变量可以通过枚举值进行初始化。

示例:

enum Weekday today = MONDAY;

(三)访问成员

枚举变量的值可以通过枚举值直接访问。

示例:

printf("Today is %d\n", today);

如果需要更直观地输出枚举值的名称,可以使用switch语句或查找表。

(四)内存布局

枚举类型的变量在内存中占用的空间大小与普通整数相同(通常为4字节)。枚举值在内存中以整数形式存储。

五、内存对齐

(一)内存对齐的概念

内存对齐是指数据在内存中的存储位置必须满足一定的对齐规则。对齐规则的目的是提高内存访问效率,减少硬件访问内存时的延迟。不同的编译器和硬件平台可能有不同的对齐规则。

(二)默认对齐规则

默认情况下,C语言的结构体和联合体的内存对齐规则如下:

结构体成员对齐

1. 第一个成员在与结构体变量偏移量为0的地址处

2. 每个成员的起始地址必须是其自身大小的倍数。

3. 结构体的总大小必须是其最大成员大小的倍数。

联合体成员对齐

1. 联合体的大小等于其最大成员的大小。

2. 联合体的起始地址必须是其最大成员大小的倍数。

(三)改变对齐规则

可以通过#pragma pack指令改变内存对齐规则。#pragma pack的语法如下:

#pragma pack(n)

其中n表示对齐单位(如1、2、4、8等)。

示例:

#pragma pack(1)
struct Example
{
    char a;       // 1字节
    int b;        // 4字节
    short c;      // 2字节
};
#pragma pack()

#pragma pack(1)的作用下,Example的大小为7字节(a + b + c),没有填充字节。

六、总结

结构体、联合体和枚举类型是C语言中重要的自定义类型,它们在数据组织和管理方面发挥着重要作用。理解这些类型的定义、初始化、成员访问方法以及相关的内存对齐规则,对于编写高效、可读性强的代码至关重要。在考试中,这些知识点是重点考察内容,需要特别注意结构体的内存对齐规则、联合体的内存共享特性以及枚举类型的定义与使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值