文章目录

  • 结构体的声明
    • 1.结构的基础知识
    • 2.结构的声明
    • 3.特殊的声明
    • 4.结构的自引用
    • 5.结构体变量的定义和初始化
    • 6.结构体内存对齐
    • 7.修改默认对齐数
    • 8.结构体传参

结构体的声明

1.结构的基础知识

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

2.结构的声明

如描述学生

//定义学生类型struct Stu{//成员变量char name[20];int age;float weight;}s4,s5,s6;//全局变量int main(){struct Stu s1;//局部变量struct Stu s2;struct Stu s3;return 0;}

3.特殊的声明

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

匿名结构体类型
举个例子

//匿名结构体类型struct {char c;int a;double d;}s1;struct{char c;int a;double d; }a[20],*p;

上面的两个结构在声明的时候省略掉了结构体标签(tag)
❓问题来了

在上面代码的基础上,下面的代码合法吗?
p = &s1;

编译器会把上面的两个声明当成完全不同的两个类型
所以是非法的

4.结构的自引用

在数据的存储中,可以使用顺序表的形式对数据进行存放;
顺序表,是计算机内存储存数据的一种方式;
即用一组地址连续的存储单元依次存储线性表中的各个元素
而除了顺序表以外,还可以使用非顺序的形式的链表的方式对数据进行存储;
即使用不同的地址分别对数据进行存储,链表内的各个数据称为 “ 结点 ”
当需要使用或查找某个数据时,只需找到最初的结点即可访问需要访问的数据;

链表与顺序表都是以线性的方式对数据进行存储
若是使用结构体,如何创建一个简单的链表?

在结构中包含一个类型为该结构本身的成员是否可以呢?

//错误示范struct Node{int data;struct Node next;}; 

这样是否可以呢?
如果可以,那sizeof(struct Node)是多少呢?

上面的代码为创建一个结构体,并对结构体进行自引用达到通过一个数据访问下一个数据。
若是如此进行编译,在结构体内部的结构体变量还存在着一个结构体,周而复始,程序将会像死递归一样不停调用该结构体;
故该段代码为错误示范✖️。

正确的自引用方法✅

struct Node{int data;//4struct Node* next;//4/8};int main(){struct Node n1;struct Node n2;n1.next = &n2;return 0;}

5.结构体变量的定义和初始化

请看代码(1)

struct S{int a;char c;}s1;struct S s3;struct B{float f;struct S s;};int main(){struct S s2 = { 100,'q' };struct S s3 = { .c = 'r',.a = 2000 };struct B sb = { 3.14f,{200,'w'} };printf("%f,%d,%c\n", sb.f, sb.s.a, sb.s.c);return 0;}


请看代码(2)

struct S{char name[100];int* ptr;};int main(){int a = 100;struct S s = { "abcdef",NULL };return 0;}

6.结构体内存对齐

在声明结构体的时候,往往不同的结构体成员不同,结构体的大小也不同,若是存在以下的结构体,他们相应的大小是多少??

struct S1{int a;char c;};struct S2{char c1;int a;char c2;};struct S3{char c1;int a;char c2;char c3;};int main(){printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));printf("%d\n", sizeof(struct S3));return 0;}

答案会是什么呢?
5 6 7 ???


那为什么和预想中的结果不一样呢?

首先得掌握结构体的对齐规则:

第一个成员在与结构体变量偏移量为0的地址处。
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数与该成员大小的较小值。

VS 中默认的值为8
Linux 中没有默认对齐数,对齐数就是成员自身的大小

结构体总大小为所有成员的对齐数中最大对齐数(每个成员变量都有一个对齐数)的整数倍。如果不够,则浪费空间对齐。
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
如果嵌套了结构体,嵌套的结构体成员要对齐自己成员中的最大对齐数的整数倍处
整个结构体大小,必须是最大对齐数的整数倍,最大对齐数包含嵌套的结构体成员中的对齐数

在这里我们可以使用offsetof宏来看一下各个成员的偏移量(使用offsetof求偏移量时,应包括头文件

#includestruct S{char c;int a;};int main(){struct S s = { 0 };printf("%d\n", offsetof(struct S, c));printf("%d\n", offsetof(struct S, a));return 0;}

我们再看一个

struct S3{double d;char c;int i;};int main(){printf("%d\n", sizeof(struct S3));return 0;}



在上段代码的基础上,我们再来一个

struct S3{double d;char c;int i;};struct S4{char c1;struct S3 s3;double d;};int main(){printf("%d\n", sizeof(struct S4));return 0;}


如果嵌套了结构体,嵌套的结构体成员要对齐自己成员中的最大对齐数的整数倍处
整个结构体大小,必须是最大对齐数的整数倍,最大对齐数包含嵌套的结构体成员中的对齐数

为什么存在内存对齐” />