结构体,是C语言中一种重要的数据类型,它允许你将不同类型的数据组合成一个整体,以便于更好地组织和处理数据。你可以将结构体看作是一个容器,里面可以存放各种类型的数据。
目录
一.结构体的声明
1.一般情况
struct 结构体名 { 数据类型 成员名1; 数据类型 成员名2; ... 数据类型 成员名n; }; //注意分号不能少
其中,struct是定义结构体的关键字,结构体名是你给这个结构体起的名字,成员名则是结构体中各个成员的名称,数据类型则指定了成员的数据类型。
举个例子,如果你想要定义一个表示学生的结构体,可以这样做:
struct Student { char name[20]; int age; float score; };
在这个例子中,我们定义了一个名为Student的结构体,它包含三个成员:name(表示学生的姓名,是一个字符数组)、age(表示学生的年龄,是一个整数)和score(表示学生的成绩,是一个浮点数)。
2.特殊声明
struct { int a; char b; float c; }x;
匿名结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
二.结构体变量的创建和初始化
定义了结构体之后,你就可以创建结构体的变量了。
创建结构体变量的语法如下:
①struct 结构体名 变量名;
#include <stdio.h> struct Stu { char name[20];//名字 int age;//年龄 char sex[5];//性别 char id[20];//学号 }; int main() { //按照结构体成员的顺序初始化 struct Stu s = { "张三", 20, "男", "20230818001" }; printf("name: %s\n", s.name); printf("age : %d\n", s.age); printf("sex : %s\n", s.sex); printf("id : %s\n", s.id); //按照指定的顺序初始化 struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥"}; printf("name: %s\n", s2.name); printf("age : %d\n", s2.age); printf("sex : %s\n", s2.sex); printf("id : %s\n", s2.id); return 0; }
②struct 结构体名 {....}变量名;
#include <stdio.h> struct Stu { char name[20];//名字 int age;//年龄 char sex[5];//性别 char id[20];//学号 }s,s2; int main() { //按照结构体成员的顺序初始化 s = { "张三", 20, "男", "20230818001" }; printf("name: %s\n", s.name); printf("age : %d\n", s.age); printf("sex : %s\n", s.sex); printf("id : %s\n", s.id); //按照指定的顺序初始化 s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥"}; printf("name: %s\n", s2.name); printf("age : %d\n", s2.age); printf("sex : %s\n", s2.sex); printf("id : %s\n", s2.id); return 0; }
三.结构体的自引用(链表)
struct Node { int data; struct Node* next; };
如果没有*是错误的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的
四.结构体内存对齐
1.对齐规则
1.结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到对齐数的整数倍的地址处。
对齐数=编译器默认的⼀个对齐数与该成员变量大小的较小值。
- V S 中默认的值为 8
- Linux中gcc没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最⼤对齐数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
struct S1 { char c1; int i; char c2; }; printf("%d\n", sizeof(struct S1));
分析如下图所示
2.修改默认对齐数
#include <stdio.h> #pragma pack(1)//设置默认对⻬数为1 struct S { char c1; int i; char c2; }; #pragma pack()//取消设置的对⻬数,还原为默认 int main() { //输出的结果是什么? printf("%d\n", sizeof(struct S)); return 0; }
五.结构体传参
结构体还可以作为函数参数进行传递
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; }
二者之间首选print2函数
原因: 函 数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销较大,所以会导致性能的下降。
结论: 结构体传参的时候,要传结构体的地址。
六.结构体实现位段
位段的声明和结构是类似的,有两个不同:
1. 位段的成员必须是 int 或char ,在C99中位段成员的类型也可以 选择其他类型。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
struct A { int _a:2; int _b:5; int _c:10; int _d:30; };
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
struct S { char a:3; //a占3个比特 char b:4; char c:5; char d:4; }; struct S s = {0}; s.a = 10; s.b = 12; s.c = 3; s.d = 4;
位段的几个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位 置处是没有地址的不能对位段的成员使⽤&操作符
七.总结
总之,结构体是C语言中一种非常灵活和强大的数据类型,它可以帮助你更好地组织和处理复杂的数据结构。