一.前言
在学习初期,我们接触了很多整型,浮点型,字符型等变量,其实结构体从整体上看和他们都差不多
其实现阶段对结构体有疑惑的无疑是
1.用的少,老是忘记怎么用
2.对结构体的知识点不是很牢,经常弄混
第一点只能自己找题目练习,第二点不用担心,看完就可以豁然开朗了
二.结构体的声明
首先先看结构体的模板:
struct stu{member - list//成员列表}variable - list;//变量列表,最后有一个分号不要忘记
例如表述一个学生
struct stu{char name[20];//名字int age;//年龄char sex[5];//性别};//分号不能丢
这里的变量其实有两种表示方法,比如可以想模板一样在后面加上变量名就可以
struct stu{char name[20];int age;char sex[5];}s,a,b;
因为之前说过他整体上和其他类型差不多,所以,也可以像其他类型一样在main函数里面创建
(细细品味这句话)
struct stu{char name[20];int age;char sex[5];}int main(){struct stu s;//创建结构体变量时,要把结构体的名字写全,不能只写一个structreturn 0;}
下面就到了容易弄混的地方了,也就是这里导致很多人忘记在结构体后面创建变量一会是变量,一会又是结构体的名字
typedef struct stu{char name[20];int age;char sex[5];}stu;//这里因为前面的typedef所以这个结构体的名字就变成了stu;//后面在创建变量的时候就可以直接使用stu了;
以上就是结构体的基本声明和创建还有一些容易弄混的地方,其实结构体还有一些特殊的声明
结构体声明的时候可以不完全声明,这种叫做匿名结构体类型
struct {//没有名字char name[20];int age;char sex[5];}stu;
从这里你可以很清楚的看到这种声明方式和上面的不一样,既然不一样,那肯定是有区别的,
区别就在于这种匿名结构体只能使用一次,也就是说它只能在结构体后面创建变量,不能在main函数中重复创建,也就是说匿名结构体创建的变量,都在main函数之外,都是全局变量
还有就是如果你创建两个匿名结构体,编译器会认为他们是不同类型的,因为他们没有确定的类型名。但是这种匿名结构体也有好处,就是嵌套在结构体的结构体为匿名的时候,可以直接访问,这样比较方便,反之还需要特殊引用去处理,这里不展示了,知道就好
三.结构体的自引用
顾名思义,就是自己引用自己
但也没有我们想的那么简单,下面看一段代码思考一下就知道了
struct stu {int age;struct stu s;};//思考这个结构体有多大?
这个结构体会大到无穷,就感觉像一个无限的递归一样,所以是不合理的
正确的应该是把第二个元素换成指针形式,这样通过指针我们就知道这个结构体是多大了,
struct stu {int age;struct stu* next;};
这里用到了数据结构的知识,知道有这么回事就行了
上面说了结构体的大小问题,那么我们是怎么去计算结构体大小的呢?
四.结构体内存对齐
首先先看下面一段代码
#define _CRT_SECURE_NO_WARNINGS 1#includeusing namespace std;struct stu {char b;int age;char a;};struct node {char a;char b;int age;};int main(){cout << sizeof(struct stu) << endl;cout << sizeof(struct node);}
计算的是他们的大小,可以先思考一下,他们的大小是否相同
这么可以看到是不一样的,这是因为结构体有自己的内存对齐规则
1.结构体的第一个成员对齐到和结构体变量的起始位置偏移量为0的地址处
2.其他成员对齐到某个数字(对齐数)的整数倍处
对齐数=编译器默认的一个对齐数和变量本身大小的较小值
3.结构体的总大小为最大对齐数的整数倍
4.如果嵌套了结构体的时候,结构体的整体大小就是所以最大对齐数的整数倍
很多人应该看完,还是一头雾水,就用上面的例子来解释
#define _CRT_SECURE_NO_WARNINGS 1#includeusing namespace std;struct stu {char b;//大小为1,默认对齐数是8(编译器不一样,默认对齐数就不一样),1<8那么1是他的对齐数int age;//同理,他的对齐数是4;char a;//对齐数是1;};//那么从第一个规则我们可以知道,第一个变量是在偏移量为0处也就是第一个位置,占一个字节//因为第二个变量对齐数是4,(2,3不是4的整数倍,但4是)所以第二个变量从第四给字节开始往后占4个字节//第三个变量对齐数是1,第二个变量以后来到了第8个字节位置,所以又8是1的整数倍,所以把它放进去//最后他们三个变量的最大对齐数是4,所以看9是否是4的整数倍(因为从0开始到8一共9个字节),是,那么它的大小就是9,不是,则往后找,直到找到4的倍数位置,所以这里找到12;struct node {char a;char b;int age;};//这里的计算可以照猫画虎,很容易就得出它的大小为8;int main(){cout << sizeof(struct stu) << endl;cout << sizeof(struct node);}
对齐规则你可以用不到,但是你不能不知道,这里只是用文字进行描述,没有画图,如果有什么疑问可以在评论区发言
五. 结构体传参
结构体传参可以分为两种,结构体传参和结构体地址传参
虽然分为两种方式,但是大多都是用的结构体地址传参,因为用结构体传参参数压栈的系统开销比较大,所以一般不用第一种
#define _CRT_SECURE_NO_WARNINGS 1#includeusing namespace std;struct stu {int age;char name[20];};void print(struct stu* p) {cout <age;//指针用->,变量用.}//void print(struct stu p){cout<<p.age;}int main(){struct stu s = { 20,"sdadasd" };print(&s);//print(s);return 0;}
六.结构体实现位段
1.位段的成员必须是unsigned int ,int ,signed int或者是char类型
2.位段的成员名后面一个冒号和一个数字
#define _CRT_SECURE_NO_WARNINGS 1#includeusing namespace std;struct stu {//开始的时候会创建一个字节的空间,然后放进去,如果不够再创建一个字节int a : 2;//这里的数字代表2位二进制位,下面同理int b : 5;int c : 10;int d : 30;};int main(){struct stu s = { 0 };s.a = 10;//转化位二进制,然后放进开辟的空间里面去,可能空间不够会有一部分截断s.b = 12;s.c = 3;s.d = 4;return 0;}
为什么要有位端呢,其实它的作用就是节省空间,但是它也有弊端,就是不能跨平台而且涉及了很多不确定的因素。
这里还有一个点就是位段的几个成员是共用一个字节的,所以我们在输入的时候,直接用scanf取地址输入是不行的,因为内存中分配一个地址,一个字节内部的bit位是没有地址的。
#define _CRT_SECURE_NO_WARNINGS 1#includeusing namespace std;struct stu {int a : 2;int b : 5;int c : 10;int d : 30;};int main(){struct stu s = { 0 };scanf("%d". & s.a);//错误//正确int x;scanf("%d", &x);s.a = x;return 0;}
如果你看到这里,那么相信你一定对结构体有一定的了解和理解了,下面就是多去练习,发现问题
希望这篇文章对大家有帮助,如果有任何疑问可以发在评论区
后面会更新动态内存和文件的相关知识,请持续关注我