1.结构体的介绍
为什么要有结构?在我学习C语言以来,在创建变量时它们的类型无非就是整型,浮点型,字符型,如果我们用来描述一个复杂的物或者人,难免有些麻烦,在这个的情况下我学习到了自定义类型:结构体,发现它真的有时候很是方便,在这我就来分享一下自己对结构体的认识,如果有什么错误,欢迎指正。谢谢啦!!
2.结构体的使用
定义
在调用或者声明时,我们得定义一下。格式如下:
struct Tag
{
char str;
int number;
};
首先 struct 是结构体的关键字,其后的 Tag 是标签这可以随便的取名(只要你觉得有价值就行),标签和关键字共同构成结构体类型。
然后就是如果类型太长,我们可以使用 typedef 来进行定义。如下
typedef struct Tag
{
char str;
int number;
}New_Name;
//另一种定义
struct Tag
{
char str;
int number;
};
typedef struct Tag New_Name;
"New_Name <==> struct Tag",可以认为它们是同一种类型。
调用
对于结构体指针,它的调用是方式是“->”,对于结构体是“.";
有了定义,那么就是用它来创建变量了,你也可以像创建整型那样,“类型 变量名” ,也可以写成下面这种:
//创建全局变量v1,其默认的初始值为0;
struct Tag
{
char str;
int number;
}v1;
int main(void)
{
printf("%d\n", v1.number);
return 0;
}
//创建局部变量v1,其默认的初始值是随机的;
struct Tag
{
char str;
int number;
}v1;
int main(void)
{
struct Tag v1;
printf("%d\n", v1.number);
return 0;
}
“v1” 就是我创建的变量,在这全局变量v1,其默认的初始值为0,局部变量v1,其默认的初始值是随机的;
初始化
对于初始化同样也有两种。
//创建全局变量v1,其默认的初始值为0;
struct Tag
{
char str;
int number;
}v1 { 'F', 4 };
int main(void)
{
printf("%d\n", v1.number);
return 0;
}
//创建局部变量v1,其默认的初始值是随机的;
struct Tag
{
char str;
int number;
}v1;
int main(void)
{
struct Tag v1 = {'F', 4 };
printf("%d\n", v1.number);
return 0;
}
在这个中是按顺序的方式进行的初始化,那么不按顺序的方式初始化就是下面的样子:
//创建全局变量v1,其默认的初始值为0;
struct Tag
{
char str;
int number;
}v1 { .number = 4 , .str = 'F'};
int main(void)
{
printf("%d\n", v1.number);
return 0;
}
//创建局部变量v1,其默认的初始值是随机的;
struct Tag
{
char str;
int number;
}v1;
int main(void)
{
struct Tag v1 = {.number = 4 , .str = 'F'};
printf("%d\n", v1.number);
return 0;
}
匿名结构体
匿名结构体,难道没有名字吗?好像是的哦。它是省略标签名,没有省略变量名,那么它还能有两种的创建变量的方式吗,不了他只有一种了。我开始也在想为啥只有一种,但转念一想,如果在main中创建变量,那不是要类型吗?,但匿名结构体没有类型构成中的标签,那么还不算结构体类型,只不过是一个关键字罢了,关键字能创建变量吗?那是不行的。这样那他只能定义这一种的类型了,
结构体的自引用
结构的自引用就是在本结构体中在创建一个和自生同类型结构的指针,但是不能创建结构体变量,我们要想如果是变量的话那,那就不是在套娃了,是指针就不同了它是固定的。
对于自引用,匿名结构体不能自引用,因为在在创建时匿名结构体的类型你不知道,所以自引用不能用于匿名结构体。
结构体的计算
结构体的大小究竟是多大呢?如下:
为何是8呢?在这我假设,在一个结构的空间,从高低值这片空间的第一个(也称0偏移量)开始,那么依次是0偏移量,1偏移量,2偏移量,3偏移量,一共4个对应int的四个字节,那么char咋开辟呢是下一个吗?不这要比较一下,char的大小是1个字节,对齐数是1,int的大小是4,对齐数就是4,对于编译器也有对齐数在vis上是8对于对齐数,gcc没有这个概念那就是自身的对齐数,对于char的开始位置也是有讲究的,它是偏移量%对齐数(vis和自身中较小的那个对齐数) = 0处开始,然后依据其的大小往后填。
对于结构体嵌套结构体,那么嵌套结构体的对齐数是在其中成员中最大的对齐数。
那么k的对齐数是多少呢,是8。
在我了解的结构体,进行这样存储,是为了更方便的访问,提高速度。还有在结构中合理的变量分布可以更好的利用空间
上面就是两种不同的排序,我们因尽量把相对较小的类型放在一起。
位段
这个结构的大小是8个字节,而实际我们只用了4个字节,其他4个浪费了,那有没有一种可能就是尽可能的浪费最少的空间呢,哎”位段“刚好就满足了这个需求。
对于位段我们尽可能使用一样的类型,不同的类型在不同的平台是不同的。
那内存是咋分配的呢?我们来看看。(注意在vis2022上)
对于这个位段 ,位段不能取地址,只支持整型,不适用于浮点数或指针类型。因为它存在一个字节存在多个变量的,你取地址只是取出一个字节的地址。
我相信你们也明白啦!!
相关的练习
#include <stdio.h>
struct _Record_Struct
{
unsigned char Env_Alarm_ID : 4;
unsigned char Para1 : 2;
unsigned char state;
unsigned char avail : 1;
};
int main(void)
{
printf("%zd\n", sizeof(struct _Record_Struct));
return 0;
}
当没有 ":值" 时就是默认的存储大小,那么这题你们可以试试。
第二题就有些难度了
int main()
{
unsigned char puc[4];
struct tagPIM
{
unsigned char ucPim1;
unsigned char ucData0 : 1;
unsigned char ucData1 : 2;
unsigned char ucData2 : 3;
}*pstPimData;
pstPimData = (struct tagPIM*)puc;
memset(puc,0,4);
pstPimData->ucPim1 = 5;
pstPimData->ucData0 = 8;
pstPimData->ucData1 = 3;
pstPimData->ucData2 = 2;
printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);//以两位显示一个十六进制的数
return 0;
}
然后就是打印了,"%02x",打印宽度为2的十六进制数字puc[0],是第一个字节,puc[1]为第二个字节,依次类推;05 16 00 00
相必大家应该有所了解,我就不在这过多阐述了。