0


C语言结构体大小的计算

C语言结构体

1.结构体的声明

要知道怎么计算结构体大小我们首先要了解结构体

1.1什么是结构体

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

1.2结构体的声明和定义

structtag{
 member-list;}variable-list;

member-list;

  • 此处是结构体成员
  • 可以有多个成员变量
  • 成员类型可以是不同类型的变量

variable-list;

  • } 之后的此处是定义的变量列表,在结构体创建初同时定义了一个结构体变量
  • 此处定义的变量是全局变量
  • 也可以在函数中 使用 struct tag abc; (tag为结构体名,abc为变量名) 方式来定义变量(局部变量)

例如此处描述一个学生:

structStu{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号}s4,s5;//分号不能丢intmain(){structStu s1;structStu s2;structStu s3;return0;}

此处的Stu就是一个结构体,内部成员描述学生的属性,s1,s2,s3,s4,s5都是定义的结构体变量.s4,s5是全局变量,s1,s2,s3是局部变量.

1.3匿名结构体

对于上述结构体的声明 有一种特殊的存在

struct{int a;char b;float c;}x;struct{int a;char b;float c;}*p;

细心的你应该发现了,上述的声明方式没有名称,这种类型叫做匿名结构体,由于没有名称,所以只会在声明结构体的时候,同时定义需要的结构体变量.

需要注意的是上述的两种结构体虽然成员内容相同,但是在内存中完全是不同的两个结构体.

所以基于上述代码
p = &x;
这样的代码是不合法的.

1.4结构体的自引用

structNode{int data;//1.struct Node next;//2.struct Node* next;};

如果结构体需要在成员中构建自己的变量的时候,应该使用 方式1 还是 方式2 呢.
如果你也觉得迷茫.那么请你看这么一段代码.

sizeof(structNode);

如果使用方式1的话,那上述代码算出来的结果应该是多少呢.
我们发现会进入无限循环,无法算出结果.这在计算机种有限资源的容器中是不允许出现的.所以方式2才会正确的方式.也就是下述代码.

structNode{int data;structNode* next;};

1.5结构体改名

结构体也是可以改名的(typedef关键字)

//结构体改名    typedefstructStudent{int id;char name[20];}Stu;intmain(){
    Stu s1;return0;}

我们可以发现在改名之后,我们可以更方便的创建结构体变量.

改名后的自引用

知道结构体可以改名后,肯定有人要问 那我可不可以,使用改名之后的结构体名称来进行自引用.也就是下述代码.

XXX
//匿名typedefstruct{int data;
 Node* next;}Node;//非匿名typedefstructNode{int data;
 Node* next;}Node;

简洁明了,不可以.这是不允许的.想要自引用,只能使用原名称的指针类型来引用.

//这是正确的引用方式typedefstructNode{int data;structNode* next;}Node;

此块代码是正确的引用方式

2.结构体的初始化

structPoint{int x;int y;}p1;//声明类型的同时定义变量p1structPoint p2;//定义结构体变量p2//初始化:定义变量的同时赋初值。structPoint p3 ={4,5};structStu//类型声明{char name[15];//名字int age;//年龄};structStu s ={"zhangsan",20};//初始化structNode{int data;structPoint p;structNode* next;}n1 ={10,{4,5},NULL};//结构体嵌套初始化structNode n2 ={20,{5,6},NULL};//结构体嵌套初始化intmain(){structNode head;//定义后初始化
    head.data=0;
    head.p = p3;
    head.next =NULL;

    Node h2 ={2,NULL,NULL};//定义时初始化}

结构体初始化有两种

  • 在定义的同时初始化
  • 在定义之初不初始化,之后在对成员分别进行初始化

而在定义之后,对成员的操作方式也是有两种

  • “.” 操作符:针对普通类型
  • "->"操作符:针对指针类型
structdate// 声明一个结构体类型{int month;int day;int year;}structstudent{int num;char name[20];char sex;int age;structdate birthday;char addr[30];}student1,*student2;intmain(){//直接使用"."操作符进行操作.
    student1.num =10;//bitthday成员也是一个结构体,可以嵌套使用"."操作符
    student1.birthday.month =5;
    student1.age =20;
    student1.age ++;//student2是指针类型,使用"->"操作符
    student2->age =18;//由于优先级问题需要带括号(&student2).num =31;}

当然指针类型也是可以使用".“操作符的.只需要解引用即可,但是注意需要带括号,因为”."操作符优先级高于解引用操作符.

3.结构体大小的计算

了解了结构体以及基本操作,那我们来看看结构体的大小应该怎么计算

3.1结构体对齐

结构体对齐规则

  1. 第一个成员在与结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。(最大对齐数不包含vs默认值)
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

3.2结构体大小的计算

structS1{char c1;int i;char c2;};printf("%d\n",sizeof(structS1));

在这里插入图片描述

灰色为浪费掉的空间.
根据上述规则

  1. 首先比较c1的大小和默认对齐数的大小. 显然c1的小以为c1的大小为1.然后根据1和当前偏移量对比,如果偏移量为0,则从当前位置开始存入数据,如果偏移量不为0,将偏移值移动到1的倍数处.存入c1的值,0位置.
  2. 然后比较下一个,也就是 i 的大小和默认值得大小的最小值,得到的值为4.然后根据4和当前偏移量比较,不为0则移动到4的倍数处.存入i的值,也就是4位置.
  3. 然后比较下一个,c2的大小和默认值得大小的最小值,得到的值为1.然后根据1和当前偏移量比较,不为0则移动到1的倍数处.存入c2的值,也就是9位置.
  4. 最后计算总大小.成员的最大对齐数为4(不包含vs的默认值),当前偏移量为9,需要对齐为4的倍数.也就是12.所以结构体大小就是0到对齐位置之前的内存大小,也就是12.

所以结构体最终大小为12.

3.3补充

可以使用#pragma pack(8)来更改默认对齐数

//在创建结构体之前设置#pragmapack(1)//设置默认对齐数为1//使用完后可以使用无参数的来还原为默认#pragmapack()//取消设置的默认对齐数,还原为默认

本文转载自: https://blog.csdn.net/m0_58154870/article/details/127466059
版权归原作者 魚小飛 所有, 如有侵权,请联系我们删除。

“C语言结构体大小的计算”的评论:

还没有评论