C语言-自定义类型:结构体,枚举,联合

mac2026-01-24  8

结构体

结构体的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

结构体的声明

例如描述一个学生:

struct Stu { char name[20]; int age; char sex[5]; char id[20]; }S;//S为变量列表 typedef struct Stu { int a; char b; float c; }s,*p;//s,*p为类型

在声明结构的时候,可以不完全的声明。

//匿名结构体类型 struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20],*p;
结构的自引用
不使用typedef时 错误的方式: struct Node { int data; struct Node next;//结构体 };

这种声明是错误的,因为这种声明实际上是一个无限循环,成员next是一个结构体,next的内部还会有成员是结构体,依次下去,无限循环。在分配内存的时候,由于无限嵌套,也无法确定这个结构体的长度,所以这种方式是非法的。 正确的自引用方式: (使用指针):

struct Node { int data; struct Node* next;//指针 };

由于指针的长度是确定的(在32位机器上指针长度为4),所以编译器能够确定该结构体的长度。

使用typedef 时 错误的方式: typedef struct { int data; Node* next;//虽然也使用指针,但这里的问题是:Node尚未被定义 } Node;

这里的目的是使用typedef为结构体创建一个别名Node。但是这里是错误的,因为类型名的作用域是从语句的结尾开始,而在结构体内部是不能使用的,因为还没定义。 正确的方式:有三种,差别不大,使用哪种都可以。

/* 方法一 */ typedef struct Node { int data; struct Node* next; } Node; /* 方法二 */ struct tag_2; typedef struct tag_2 Node; struct tag_2 { int data; Node* next; }; /* 方法三 */ struct tag_3 { int data; struct tag* next; }; typedef struct tag_3 Node;
结构体变量的定义和初始化
struct Point { int x; int y; }p1;//声明类型的同时定义变量p1 int main() { struct Point p1; p1.x = 1; p1.y = 2; return 0; } struct stu { char name[15]; int age; }; struct stu s = { "fandebiao", 45 };//初始化 struct Node { int data; struct point p; struct Node* next; }n1 = { 10, {4,5},NULL };//结构体嵌套初始化 struct Node n2={20,{5,6},NULL};//结构体嵌套初始化
结构体的使用
结构体可以使用 . 操作符来访问其成员结构体的每个成员都是一个单独的变量,拥有单独的空间,可以单独的去修改结构体中的数组在赋值时可以进行数组的一个一个元素赋值,也可以使用strcpy函数进行对字符数组赋值,千万不能使用 数组名=字符串 的方式。

介绍了以上结构体的使用规则,我举两个例子:

strcpy(student.name,"ningbo"); student.age = 20; strcpy(student.sex,"female");

这里我将 student 结构体变量的所有成员的值都改了一下。 然后再介绍结构体的一种访问成员的方法:

结构体指针可以直接通过 -> 操作符来访问结构体的成员

首先得有一个结构体指针:

struct stu* pstudent; //定义一个struct stu型的指针 pstudent = &student; //将结构体student的地址放在结构体指针pstudent里面

然后再进行指针直接访问:

strcpy(pstudent->name,"liubei"); pstudent->age = 18; strcpy(pstudent->sex,"male");

这里通过结构体指针pstudent将结构体student的成员又进行了修改。

结构体内存对齐

结构体的对齐规则:

第一个成员在与结构体变量偏移量为0的地址处。其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 (对齐数= 编译器默认的一个对齐数 与 该成员大小的较小值。) VS中默认的值为8Linux中的默认值为4 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所 有最大对齐数(含嵌套结构体的对齐数)的整数倍。 //求结构体的大小 struct s1 { char c1; int i; char c2; }; struct s2 { char c1; char c2; int i; }; struct s3 { double d; char c; int i; }; struct s4 { char c1; struct s3 s3; double d; }; int main() { printf("%d\n", sizeof(struct s1));//12 printf("%d\n", sizeof(struct s2));//8 printf("%d\n", sizeof(struct s3));//16 printf("%d\n", sizeof(struct s4));//32 }

利用 #pragma 这个预处理命令,可以改变我们的默认对齐数。

#pragma pack(1)//设置默认对齐数为1 struct S2 { char c1; int i; char c2; }; #pragma pack()//取消设置的默认对齐数,还原为默认

总体来说:

结构体的内存对齐是拿空间来换取时间的做法。设计结构体时,让占用空间小的成员尽量集中在一起。结构在对齐方式不合适时,可以自己更改默认对齐数。
结构体传参
struct s { int data[1000]; int num; }; struct s s = { { 1, 2, 3, 4 }, 1000 }; //结构体传参 void print1(struct s s) { printf("%d\n", s.num); } //结构体地址传参 void print2(struct s* ps) { printf("%d\n", ps->num); } int main() { print1(s);//传结构体 print2(&s);//传地址 return 0; }

总结:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。结构体传参的时候,要传结构体的地址。
位段

位段的声明和结构体是很类似的,只是成员的类型有区别:

位段的成员必须是 int, unsigned int 或 signed int。位段的成员名后边有一个冒号和一个数字,数字代表该成员所占的比特位(bit)。 struct A { int _a : 2; int _b : 5; int _c : 10; int _d : 30; }; struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; int main() { printf("%d\n", sizeof(struct A));//8 printf("%d\n", sizeof(struct S));//3 return 0; }

枚举

枚举顾名思义就是——列举,相当于创建一个自定义类型,用这个类型创建的变量只能取你自己设定的范围内的值,但这只是一个符号的定义,实际上都是 int 型的。

enum color { red, green, blue, }; enum color clr=green;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。 clr=5; int main() { printf("%d\n", green);// 5 return 0; }

{}中的内容是枚举类型的可能取值,也叫 枚举常量。 枚举常量都是有值的,默认从0开始依次递增1,当然在定义的时候也可以赋初值。 枚举的优点

增加代码的可读性和可维护性和#define定义的标识符比较枚举有类型检查,更加严谨。防止了命名污染(封装)便于调试使用方便,一次可以定义多个常

联合体(共用体)

联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以 联合也叫共用体)

联合类型的声明

union Un { char c; int i; };

联合变量的定义

union Un un;

计算联合体变量的大小

int main() { printf("%d\n", sizeof(un));//4 return 0; }

联合大小的计算

联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。 union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; int main() { printf("%d\n", sizeof(union Un1));//8 printf("%d\n", sizeof(union Un2));//16 return 0; }
最新回复(0)