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