C语言、结构体
📚 目录
- 1. 结构体类型声明定义
- 2. 结构体的创建和初始化
- 3. 结构体内存大小
- 4. 结构体的传参
- 5. 位段
前言:
C语言也给我们程序员规定了一种自定义类型,结构体的每一个成员可以是不同类型的变量,结构是⼀些值的集合,这些值称为成员变量,那么结构体的内存大小又是怎么算的呢?
1. 结构体类型声明定义
结构是⼀些值的集合,这些值称为成员变量;关键字:struct
#include<stdio.h>structadd//结构体的声明定义{inta;}value;//value指的是这个结构体的名字 旁边这个分号必须要添加 value可以先不写,等我们需要的时候创建就行了//比如intmain(){structadds1;//我们创建的时候就能够这么写return0;}这就是简单的结构体声明
假设当我们想要描述一个学生的时候。
structstu{charname[20];intage;floatScore;};这就简单创建了一个简单结构体类型。
2. 结构体的创建和初始化
就拿我们的学生这个结构体来讲
#include<stdio.h>structstu//结构体声明定义{charname[20];intage;floatScore;};intmain(){//struct stu s1;这个是结构体的创建,类型位struct stu 名字位:s1//结构体的创建和初始化structstus1={"zhangsan",20,95.5f};//这就是我们简单的初始化,也可以部分初始化,或者不初始化。//当我们想不按照这个顺序初始化的时候我们就要用到这个操作符structstus2={.age=20,.Score=95.5f,.name="zhangsan"};return0;}结构体的打印:
#include<stdio.h>structstu//结构体声明定义{charname[20];intage;floatScore;};intmain(){structstus2={.age=20,.Score=95.5f,.name="zhangsan"};//打印printf("%d\n",s2.age);printf("%f\n",s2.Score);printf("%s\n",s2.name);return0;}结构的特殊声明:
比如:
//这样类型的特殊声明struct{charname[20];intage;floatscore;}value;//还有这样的结构体声明struct{charname[20];intage;floatscore;}value[10],*p;这样的声明省略掉了我们结构体的名字,直接就创建了我们的变量。
那么这样做编译器会不会报错呢?
答案:不会报错
警告:
编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。
结构体类型的自引用:
学到数据结构的时候我们就会知道链表这个概念。
就又没有想过,结构体里面包含结构体,一个连接着一个,形成一个链条一样的链表。
举例:
//就像这样写structNode{inta;structNode*next;};以上就是我们的结构体的创建和初始化以及特殊的结构体声明定义。
3. 结构体内存大小
那么结构体的内存大小又是怎么算的呢?
接下来我们来学习一下。
structstu{charname[20];intage;};这个结构体内存大小多大呢?
答案:24
为什么是24呢?我们就要了解最大对齐数这个概念。
对其规则
最大对齐数:
结构体的第1个成员对齐到和结构体变量起始位置偏移量为0的地址处。
从第2个成员变量开始,都要对齐到某个对其数的整数倍的地址处,(对其数 = 编译器默认的⼀个对其数与该成员变量大小的较小值。)
结构体总大小为最大对齐数的整数倍。
嵌套了结构体的情况下,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构
体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
举例:
注意:每一个编译器默认的对齐数都是不一样的,就像我们Vs这个编译器默认的对齐数为:8
#include<stdio.h>structS1{charc1;//对齐数位1inti;//对齐数为4charc2;//对齐数为1};intmain(){printf("%zu\n",sizeof(structS1));//选择最大对齐数的整数倍return0;}修改默认对齐数
#include<stdio.h>#pragampack(1);//设置默认对齐数为1structS{charc1;//对齐数位1,而我们默认对齐数为1 , 对齐数为1inti;//对齐数为4,而我们默认对齐数为1 , 对齐数为1charc2;//对齐数为1,而我们默认对齐数为1 , 对齐数为1};#pragmapack();取消修改默认对齐数intmain(){printf("%d\n",sizeof(structS));return0;}得到的结果都是我们的1000。这样我们就能知道:结构体传参的时候,要传结构体的地址
4.结构体的传参
结构体又是怎么传参的呢?
#include<stdio.h>structS{intdata[1000];intnum;};//结构体传参//测试传结构体voidtest1(structSs){printf("%d\n",s.num);}//测试地址voidtest2(structS*s){printf("%d\n",s->num);}intmain(){structSs={{1,2,3,4},1000};test1(s);//传结构体test2(&s);//传地址return0;}得到的结果都是我们的1000。这样我们就能知道:结构体传参的时候,要传结构体的地址
5.位段
什么是位段?
位段:
1. 位段的成员必须是int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他整型家族类型,比如:char。
2. 位段的成员名后边有⼀个冒号和⼀个数字。
举例:
structA{int_a:2;int_b:3;int_c:30;};intmain(){printf("%d\n",sizeof(structA));return0;}这就是我们位段的写法。
A就是⼀个位段类型。
位段的内存分配:
1. 位段的成员可以是int、 unsigned int、signed int 或者是char 等类型
2.位段的空间上是按照需要4个字节或者一个字节的方式开辟
3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
例如:
#include<stdio.h>structS{chara:3;charb:4;charc:5;chard:4;};intmain(){structSs={0};s.a=10;s.b=12;s.c=3;s.d=4;return0;}
注意:
位段在跨平台的时候会出现问题。
问题:
int 这个位段被别的编译器当成有符号数还是无符号数是不确定的。
位段中最大位的数不能确定。
位段中的成员在内存中从左向右分配,还是从右向左分配
当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,无法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利用,这是不确定的。
总结:位段可以达到结构体那样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
完!
