1.结构体范例概述
结构体范例是一种用户自定义的数据范例,用于将不同范例的数据组合成一个整体。在C语言中,结构体利用struct关键字定义,由一系列具有相同范例或不同范例的数据构成的数据聚集,也称为结构。结构体中的数据在逻辑上是相互关联的,每个数据称为结构体的成员,成员可以有不同的数据范例,而且成员一般通过名字举行访问
2.结构体范例的特点
每个成员具有独立的数据范例:这意味着可以在一个结构体内混淆不同范例的数据,如整数、浮点数、字符数组等。
定义结构体时,成员数量必须固定:一旦结构体范例被定义,其成员的数量和范例就不能改变。
成员名固定唯一且不可与结构体范例相同:每个成员必须有一个唯一的名称,且这个名称不能与结构体范例名相同。
- 结构体范例的声明
- 结构体范例的自引用
- 结构体范例变量的定义、初始化和访问
- 结构体内存对齐
- 结构体传参
3. 结构体范例的声明
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- //方法一
- struct Stu
- {
- char name[20];
- float score;
- int age;
- }s1,s2;//全局变量
- int main()
- {
- //方法二
- struct Stu s3, s4;//全局变量
- return 0;
- }
复制代码 在C语言中,结构体(struct)是一种用户自定义的数据范例,它答应将多个不同范例的变量组合在一起。当你频繁地利用结构体时,每次都输入struct可能会显得繁琐。为相识决这个题目,C语言提供了typedef关键字,可以对结构体范例举行重命名,从而简化结构体的利用
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- //方法一
- typedef struct
- {
- char name[20];
- float score;
- int age;
- }Stu;//全局变量
- int main()
- {
- //方法二
- Stu s3, s4;//全局变量
- return 0;
- }
复制代码 注:s1,s2,s3,s4是结构体变量
4. 结构体范例的自引用(引用同范例的下一个节点)
我们先来看一下链表中的数据结构:
我们起首来定义一个整型数组:int arr[]={ 1, 2, 3, 4, 5 };
那么在结构体的自引用中我们用到的就是链表
我们通过结点在结构体中寻找下一个结点
- struct Node
- {
- int data;
- struct Node next;
- };
复制代码 如果我们这样写,就有一个大题目,就是这个结构体的大小不确定,如果结点过多,大小会一直大下去
那么我们怎么来优化呢?
解答:我们可以利用指针,因为指针在编译器中的大小是固定的4/8字节
- struct Node
- {
- int data;
- struct Node* next;
- };
复制代码
5. 结构体范例变量的定义、初始化和访问
a. 结构体指针的定义
- #define _CRT_SECURE_NO_WARNINGS 1
- //方法一
- struct Point
- {
- int a;
- int b;
- }p1;
- //方法二
- struct Point p2;
- int main()
- {
- //方法三
- struct Point p3;
- return 0;
- }
复制代码 b. 结构体指针的初始化
- #define _CRT_SECURE_NO_WARNINGS 1
- //方法一
- struct Point
- {
- int a;
- int b;
- }p1 = {1,2};
- //方法二
- struct Point p2 = {3,4};
- int main()
- {
- //方法三
- struct Point p3 = {5,6};
- return 0;
- }
复制代码 c. 结构体指针的访问
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- struct Point
- {
- int x;
- int y;
- };
- struct Stu
- {
- int num;
- char ch;
- struct Point p;
- float d;
- };
- int main()
- {
- //顺序访问
- struct Stu s1 = { 100,'a',{2,5},3.3 };
- //乱序访问
- struct Stu s2 = { .d = 8.9,.num = 245,.ch = 'h',.p.x = 55,.p.y = 78 };
- return 0;
- }
复制代码
6. 结构体内存对齐
我们先来看一下这个小题
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- struct S1
- {
- char c1;
- int a;
- char c2;
- };
- struct S2
- {
- char c1;
- char c2;
- int a;
- };
- int main()
- {
- printf("%d\n", sizeof(struct S1));
- printf("%d\n", sizeof(struct S2));
- return 0;
- }
复制代码 输出效果:
在我们的认识中,char为1个字节,int为4个字节,那么两个都应该是6才对,那为什么确实不为6且不相同的数呢?
解答:
a. 结构体内存对齐规则
- 第一个成员的对齐:结构体的第一个成员通常从偏移量0的地址开始存储。
- 后续成员的对齐:其他成员变量的存储地址需要是对齐数的整数倍。
对齐数是编译器默认的对齐数和该成员自身大小的较小值。
- 结构体总大小的对齐:整个结构体的大小也会按照最大对齐数的整数倍举行对齐。这个最大对齐数是全部成员中自身对齐值最大的那个,以及指定对齐值中较小的一个。
- 嵌套结构体的对齐:如果结构体内部有嵌套的结构体,那么嵌套的结构体也会按照上述规则举行对齐,终极结构体的整体大小将是全部最大对齐数(包罗嵌套结构体的对齐数)的整数倍。
- 编译器默认对齐数:在不同的编译器和平台上,默认的对齐数可能不同。例如,在Linux体系中,默认对齐数可能是4字节,而在某些64位体系或Visual Studio编译器中,默认对齐数可能是8字节
在这里我们可以看见这里只占了9个内存,还不足以满足对齐数的全部条件
b. why:为什么存在内存对齐数呢?
ⅰ. 提高CPU访问数据的效率
内存对齐可以提高CPU访问数据的效率。这是因为CPU在访问内存时通常是以固定大小的块(如4字节或8字节)举行读取的。如果数据按照特定的对齐规则举行排列,CPU可以在一次访问中读取到完备的数据块,从而淘汰访问次数,提高效率。例如,如果一个int范例的数据位于4字节的边界上,那么在32位体系中,CPU可以在一个读周期内读取到这个int数据,而不需要举行额外的操作来拼接数据2。
ⅱ. 兼容性和可移植性
不同的硬件平台可能有不同的内存访问限定。有些平台只能在特定地址上访问特定范例的数据,否则可能会引发硬件异常。内存对齐可以确保步调在不同硬件平台上的一致性和可移植性。例如,Windows操作体系默认的对齐数是8,而Linux默认的对齐数是4。
ⅲ. 淘汰内存碎片
内存对齐有助于淘汰内存碎片的产生。通过将数据按照对齐规则举行排列,可以制止数据分散在内存中的各个角落,形成难以利用的小块内存,从而提高内存的利用率3。
ⅳ. 结构体和联合体的对齐规则
在C和C++编程中,结构体和联合体的成员通常需要按照一定的对齐规则举行排列。这些规则包罗数据成员的起始位置、结构体成员的对齐方式以及结构体总大小的对齐方式。通过对齐,可以确保结构体和联合体在内存中的结构符合特定的硬件平台要求,从而提高步调的实行效率和可移植性
c. 修改默认对齐数
我们用到 #pragma
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #pragma pack(1)//设置默认对齐数
- struct S1
- {
- char c1;
- int a;
- char c2;
- };
- #pragma pack()//恢复默认对齐数
- struct S2
- {
- char c1;
- char c2;
- int a;
- };
- int main()
- {
- printf("%d\n", sizeof(struct S1));
- printf("%d\n", sizeof(struct S2));
- return 0;
- }
复制代码 输出效果:
7. 结构体传参
a. 结构体传参分为两个方式
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- struct S
- {
- int a;
- int b;
- };
- struct S s= { 4,5 };
- //结构体传参
- void Print1(struct S s)
- {
- printf("%d\n", s.a);
- }
- //结构体地址传参
- void Print2(struct S* pc)
- {
- printf("%d\n", pc->b);
- }
- int main()
- {
- Print1(s);
- Print2(&s);
- return 0;
- }
复制代码 那么结构体传参和结构体地址传参我们保举哪一个呢?
答:保举结构体地址传参
b. 为什么保举结构体地址传参
在C语言中,结构体是一种复合数据范例,它可以包含多个不同范例的成员。当需要将结构体作为参数通报给函数时,有两种重要的方式:传值和传地址。下面我们将探讨为什么通常保举利用结构体地址传参。
- 淘汰内存开销
当通过值通报结构体时,函数会收到结构体的一个完备副本。如果结构体非常大,这可能会导致大量的内存被不须要的复制,增加了内存开销。相比之下,通过地址通报结构体只需要通报一个指向结构体的指针,这通常只需要4字节(32位体系)或8字节(64位体系),大大淘汰了内存占用
- 提高性能
由于通过值通报结构领会创建结构体的副本,这不但斲丧更多的内存,还会增加CPU的负担,因为它需要额外的时间来举行数据复制。而通过地址通报则制止了这种复制,提高了步调的实行效率
- 改变原始数据
如果希望在函数内部对结构体的成员举行修改,而且这些修改应该反映在调用函数的上下文中,那么必须通过地址通报结构体。因为通过值通报只会通报结构体的副本,任何对副本的修改都不会影响原始结构体
- 代码简洁性
通过地址通报结构体可以使代码更加简洁和易读。例如,可以利用指向结构体的指针来调用结构体的方法,这在面向对象编程风格的C代码中很常见
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |