一、结构体
解释:在C语言中,结构体(Struct)是一种用户自定义的数据类型,用于将不同类型的数据元素组合在一起,以便作为一个单独的数据单元来处理。结构体允许创建一个包含多个字段或成员的数据结构,每个字段可以是不同的数据类型,例如整数、字符、浮点数等。这使得结构体非常有用,因为它们允许表示和组织复杂的数据。
1.如何声明
要定义一个结构体,可以使用这样的语法:
struct 结构体名称 {
数据类型 成员1;
数据类型 成员2;
// 更多成员...
};
例如,描述一个学生:
struct Student {
char name[50];
int age;
float gpa;
};
要创建结构体变量,可以像这样使用结构体名称:
struct Student student1;
要访问结构体成员,可以使用成员选择运算符(.):
strcpy(student1.name, "John");
student1.age = 20;
student1.gpa = 3.5;
结构体也可以引用本身当作成员:
struct Node
{
int data;
struct Node* next;
};
如果这里next是一个struct Node类型的变量,那结构体将无法计算大小,非法。
2.结构体大小计算(结构体内存对齐)
一个问题:
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));
这两个结构体的大小分别是12和8。
为什么同样的成员变量的结构体只是因为顺序不同,大小却不同,这就涉及到结构体的内存对齐。
结构体内存对齐是用于确保结构体中的成员按照特定的规则存储在内存中,以提高内存访问的效率。结构体内存对齐是为了避免访问未对齐内存地址而引发的性能问题和可能的硬件异常。
内存对齐的基本原则包括:
1.每个结构体成员应该按照其数据类型的大小对齐。例如,整数通常需要按照它们的大小(例如,4字节或8字节)对齐,字符需要按照1字节对齐。
2.结构体的起始地址应该是结构体成员中最大的对齐要求的倍数。这确保了结构体的整体对齐。
3.结构体的大小应该是其最大成员的大小的倍数,以便多个结构体实例在内存中排列时不会导致不必要的浪费。
这就能解释了S1与S2的大小必须是int类型大小(4)的倍数,在S1中c1占4个,i占4个,c2占4个,在S2中c1和c2一共占4个,i占4个。
也可以通过 #pragma来设置默认对齐数。
#pragma pack(8)//设置默认对齐数为8
#pragma pack()//取消设置的默认对齐数,还原为默认
这种内存对齐方式确保了结构体的每个成员都可以被高效地访问,同时也避免了不必要的性能损耗。在实际编程中,程序员通常不需要手动插入填充字节,编译器会自动处理这些细节。但要了解内存对齐的概念,以便更好地理解和优化代码的内存访问。
二、位段
位段是C语言中的一种数据结构,用于将数据按位存储在内存中,允许程序员精确地控制数据的位大小和位操作。位段通常用于节省内存空间或与硬件寄存器进行交互,以表示硬件控制寄存器的不同位。
位段的定义类似于结构体,但与结构体不同的是,位段的成员可以指定占用的位数,且位段的成员必须是 int、unsigned int 或signed int。它的一般语法如下:
struct {
数据类型 成员名称: 位数;
// 更多位段成员...
} 结构体变量;
例如:(冒号后为指定占用的位数)
struct Date {
unsigned int day : 5; // 5位表示日
unsigned int month : 4; // 4位表示月
unsigned int year : 12; // 12位表示年
};
如何计算它的大小呢?
1.计算位段成员的总位数:将所有位段成员的位数相加,这将给出位段的总位数。
2.确定最小的整数类型:找到足够大以容纳位段的总位数的最小的整数类型。例如,如果总位数为17位,那么可以使用 unsigned int,因为它通常是16位的倍数。
例如这个例子:
a 占用5位,b 占用3位,c 占用6位。
总位数:5 + 3 + 6 = 14位。
找到足够大以容纳14位的最小整数类型是 unsigned int。
unsigned int 的大小通常是4字节(32位),所以位段的大小是4字节。
三、枚举
枚举类型是一种C语言中的用户自定义数据类型,用于定义一组具名的整数常数。枚举类型允许您为一组相关的常数分配友好的、易于理解的名称,而不是使用硬编码的数字。这使得代码更具可读性和可维护性。
要定义一个枚举类型,可以使用以下语法:
enum 枚举类型名称 {
枚举成员1,
枚举成员2,
// 更多枚举成员...
};
例如,以下是一个表示星期几的枚举类型的示例:
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
在这个示例中,Weekday 是枚举类型的名称,而 Monday、Tuesday 等是枚举成员的名称。默认情况下,第一个枚举成员的值为0,后续成员的值依次递增。
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 防止了命名污染(封装)
- 便于调试
- 使用方便,一次可以定义多个常量