在学习 C 语言的早期阶段,我们首先熟悉了基本的数据类型,如 int、double、float 等。然而,随着我们深入学习,逐渐认识到这些基本类型有时无法完整地表达一些复杂概念,比如时间。时间的表示需要涵盖年、月、日、时、分、秒等诸多信息,这显然超出了基本数据类型的限制。因此,为了更准确地描述时间,我们可以利用结构体的特性创建自定义类型,其中包含多个成员变量,用于存储各种时间信息。这一时刻,自定义类型成为我们未来学习道路上不可或缺的利器。
环境
vs2022 x64 debug
枚举
enum color {
RED,
GREEN,
BLUE
};
定义一个color类型,枚举类型的语法就是这样,变量之间用" , " 相隔,变量名在enum后
枚举类型大小
我们可以通过定义一个枚举类型,用sizeof计算出他的大小,我们可以看到与int的大小是一样的都是4个字节
特殊
枚举类型特殊的一点是,我们定义在枚举类型的变量是用数字来代替
联合体
在 C 语言中,联合体(union)是一种特殊的数据类型,它允许在同一内存位置存储不同类型的数据。与结构体不同的是,联合体中的每个成员共享同一块内存空间,因此联合体的大小取决于其最大的成员的大小。这意味着在任何给定的时刻,联合体只能容纳其中一个成员的值。
union c
{
char i;
int a;
};
我们可以使用typedef对联合体类型取别名
联合体类型大小
我们定义了两个相同的联合体变量,分别对他的两个不同变量赋值,但是最后我们输出的联合体大小是一样的,这就说明,联合体的大小是固定的,跟我们使用联合体的哪个变量没有关系,在第二张图中,联合体变量大小就变成了double类型的大小,二第一张图是int类型的大小,我们可以很容易看出,联合体大小是和联合体变量中最大的那一个有关
特殊
联合体类型的两个变量占用的是同一块空间,我们可以在内存中查看到
结构体
在 C 语言中,结构体(structure)是一种用户定义的数据类型,它可以包含多个不同类型的成员变量。结构体允许我们将多个相关的数据项组合在一起,形成一个逻辑上的整体,从而更方便地管理和操作数据。
#include <stdio.h>
// 定义一个结构体表示学生信息
struct Student {
int id;
char name[50];
float gpa;
};
int main() {
// 声明一个结构体变量
struct Student s1;
// 向结构体的成员赋值
s1.id = 12345;
strcpy(s1.name, "John");
s1.gpa = 3.75;
// 输出结构体的成员值
printf("Student ID: %d\n", s1.id);
printf("Student Name: %s\n", s1.name);
printf("Student GPA: %.2f\n", s1.gpa);
return 0;
}
结构体的主要优点包括
结构体允许将多个相关的数据项组织在一起,形成一个逻辑上的整体,使得数据更易于管理和操作。可以将结构体作为函数的参数,从而方便地传递和操作复杂的数据结构。同时,结构体允许使用有意义的命名来表示数据的不同部分,从而提高了代码的可读性和可维护性。
结构体类型的大小
结构体的大小取决于其成员变量的大小以及对齐方式。在 C 语言中,结构体的大小通常是其成员变量中最大的成员变量的大小的倍数,并且结构体的地址通常是其成员中最大成员的大小的倍数。
具体地说,C 标准并没有规定结构体的内存对齐方式,这由编译器的实现决定。但是大多数编译器会使用某种默认的对齐方式,通常是按照成员变量的大小进行对齐,使得结构体成员的地址是其大小的倍数。
我们可以看到,在结构体中,我们对变量不同的摆放位置不同,结构体的大小就不同,这就说明,编译器处理结构体的数据有其他的规则,而不是简单的把他们的变量大小加在一起分配空间。
这是因为编译器为了节省效率,而这个效率怎么节省的呢。
为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。不同编译器的默认对齐也不一样 vs2022的默认对齐大小是对于 32 位平台,默认对齐大小通常是 4 字节,而对于 64 位平台,默认对齐大小通常是 8 字节。
结构体的使用
typedef struct List {
int* data;
int A;
Node* next;
int B;
} Node, *NodePtr, &NodeRef;
我们用typedef对结构体重命名,对List重命名为Node 对List* 重命名为NodePtr 同理 对List& 重命名为NodeRef 也就是 本身,指针与引用,在我们学习C语言链表的时候,可能会看到这样的函数
void ListInit(List* &head)
这就是为了让我们好理解,没有写成二级指针,但是他们可能没有想到,我们还没有学到c++(&)引用,这里我们理解了就好,在C++中,Node* next就可以不用这样写,(C语言这样写更简洁,不用将整个结构体类型全部写出来),但是C++中支持直接用类名!!
对于结构体的学习,他就是一个我们定义的包含不同类型数据的类型,与其他C语言定义的类型使用是一样的,只不过他的类型是
struct List;//或者直接用我们typedef的名字
Node
对于他的指针什么的,用我们重命名的或者就正常使用就可以了