一.前言
在学习初期,我们接触了很多整型,浮点型,字符型等变量,其实结构体从整体上看和他们都差不多
其实现阶段对结构体有疑惑的无疑是
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;//创建结构体变量时,要把结构体的名字写全,不能只写一个struct
return 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
#include<iostream>
using 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
#include<iostream>
using 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
#include<iostream>
using namespace std;
struct stu {
int age;
char name[20];
};
void print(struct stu* p) {
cout << p->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
#include<iostream>
using 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
#include<iostream>
using 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;
}
如果你看到这里,那么相信你一定对结构体有一定的了解和理解了,下面就是多去练习,发现问题
希望这篇文章对大家有帮助,如果有任何疑问可以发在评论区文章来源:https://www.toymoban.com/news/detail-837937.html
后面会更新动态内存和文件的相关知识,请持续关注我文章来源地址https://www.toymoban.com/news/detail-837937.html
到了这里,关于【c语言】——结构体千字解读,看完你肯定不会有任何结构体的疑惑的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!