结构体_C语言

1.1 什么是结构体?

结构体是一个复杂对象,我们都知道,C语言的基本数据类型有 int,char,float,double …等等。 int类型可以表示年龄,double类型可以表示考试分数。但是如果我们要描述一个人,人有各种各样的特征,是一个复杂对象。这时基本数据类型就无法满足我们的需求,所以我们需要用结构体来描述一个复杂对象。

1.2 结构体的定义

那么我们如何定义一个结构体呢?结构体的定义格式:

struct 结构体类型名
{
结构体成员;

}结构体变量名(可以不写,写了该变量默认全局变量);

那我们用代码演示一下。

struct Student //结构体类型名
{
	//结构体成员
	int age; 
	char neme[20];
	double score;
}stu1; //结构体变量名(也是创建结构体对象)

这样,我们的一个结构体类型就定义好了,但仅仅只是定义的这个类型,如果需要用到这个类型,我们还需要创建变量。 就像 int 这个类型本身存在,我们要使用的时候需要 int i 来创建,结构体也是一样的。

struct Student
{
	int age;
	char neme[20];
	double score;
}stu1;

int main()
{
	int i ; //创建一个整形变量
	struct Student stu2;//创建一个结构体变量
	return 0;
}

实际上stu1 和stu2唯一的区别就是,stu1是全局变量,stu2是局部变量

1.3 结构成员的访问

当我们创建了一个结构体的时候,我们需要去访问结构体里面的成员,访问结构体成员有两种方式。

1.结构体变量名.结构体成员名
2.结构体指针->结构体成员名

那么我们用代码来演示一下。

#include<stdlib.h>
#include<string.h>
struct Student
{
	int age;
	char name[20];
	double score;
}stu1;

int main()
{
	struct Student stu2;
	//1.结构体变量名.结构体成员名
	stu1.age = 10; //给结构体成员赋值
	strcpy(stu1.name,"zhangsan");
	stu1.score = 88.5;
	printf("%d %s %.1lf\n", stu1.age, stu1.name, stu1.score);

	//2.结构体指针->结构体成员名
	struct Student* ps = &stu2;
	ps->age = 18; //通过结构体指针给成员赋值
	strcpy(ps->name, "lisi");
	ps->score = 85.5;
	printf("%d %s %.1lf\n", ps->age, ps->name, ps->score);

	return 0;
}

在这里插入图片描述
这样我们就成功的访问到了结构体成员,并且还能给结构体成员赋值。

1.4 结构体的内存对齐

那么一个结构体的大小是多少呢?我们先来看看下面2个结构体的大小。

#include<stdlib.h>

struct s1
{
	char c1;
	int i;
	char c2;
};

struct s2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	printf("s1 = %d\n",sizeof(struct s1));
	printf("s2 = %d\n", sizeof(struct s2));

	return 0;
}

在这里插入图片描述
我们可以看到,s1结构体的大小是12,s2结构体的大小是 8 ,可是它们的成员明明一样,为什么大小不一样? 那是因为结构体存在内存对齐。
结构体的内存对齐规则

1.第一个成员在结构体变量偏移量为0的地址处
2.其他成员变量要对齐某个数字(对齐数)的整数倍地址处
3. 结构体的总大小为最大对齐数(每个成员都有一个对齐数)的整数倍
4.如果结构体嵌套,嵌套的结构体对齐到它本身的最大对齐数的整数倍

我们了解了对齐规则,那么现在就来解释一下 为什么s1是12,s2是8。
在这里插入图片描述
c1是结构体的第一个成员,所以放在偏移量为0的位置
在这里插入图片描述
随后我们要存放 i ,因为i是4个字节,而vs默认的对齐数是8个字节,取较小的那一个,所以 i的对齐数是4,因此我们要把i放4的整数倍地址处,而偏移量1,2,3都不是4个整数倍,所以int应该放在偏移量为4的位置。也就是说上面三块空间浪费了,如下图。
在这里插入图片描述
现在已经放进了c1和i,接下来我们放c2,c2是char类型,占1个字节,所以对齐数是1,所以存放在1的倍数的地址偏移量处(任意)。
在这里插入图片描述
这时候我们的结构成员全部放完,占了9个字节,可是第三点说,结构体的总大小必须是成员中最大对齐数的整数倍。而
char c1 1个字节,对齐数为1
int i 4个字节 对齐数为4
char c2 1个字节,对齐数为1
所以成员中的最大对齐数是4,也就是结构体的总大小必须是4的倍数。所以下面9,10,11偏移量 的空间就浪费了。
在这里插入图片描述
那们我们在根据这个方法,来算s2的大小。
在这里插入图片描述
首先,我们把c1 放进偏移量为0的位置。
在这里插入图片描述

然后我们放c2,因为c2是一个字节,vs默认对齐数为8,所以c2的对齐数是1,可以放在所有1 的倍数的偏移量处。所以c2可以直接放在偏移量为1 的地方。
在这里插入图片描述
然后我们放i,i 是一个int类型,占四个字节,所以对齐数是4,要放在4的整数倍的地址偏移处。也就是偏移量为4的地方。
在这里插入图片描述
这样我们的成员就放完了,此时整个结构体的大小是8个字节,最大对其实是int,四个字节,总大小是最大对对齐数的整数倍。所以,s2的字节大小是8。

1.5 位段

位段的声明和结构体是类似的,但有两点不同。

1.位段的成员必须是int,unsigned int,signed int
2.位段的成员名后面有一个冒号和一个数字,数字代表占的比特位。

以下就是位段的创建

#include<stdio.h>

struct s1
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

int main()
{

	return 0;
}

我们可以看到这个位段,占3个字节
在这里插入图片描述
接下来我们分析一下,它为什么只占三个字节。
首先,我们说过,冒号后面的数字代表的是所占的比特位。
而一开始的成员类型是char , 所以我们会开辟一个字节的空间。
然后我们把a放进,a占了3个比特位,因为是小端存储,所以低字节序存放在低地址处,所以后面三个比特位是a
在这里插入图片描述
接下来b占4个比特位。
在这里插入图片描述
然后c占5个比特位,但是这只有1比特位了,不够,那么就在开辟一个字节的空间。
在这里插入图片描述
那么d占4个比特位,又不够了怎么办?我先把d改成3个比特位,看大小是不是2。
在这里插入图片描述
d改成3后,那么就是2个字节,d是4的时候,是3个字节。那么当d4个字节不够放进第二个字节时。那么会继续开辟一个字节的空间,而第一个字节剩下的一个和第二个字节剩下的3个,不会再被利用,也就是说这四个比特位浪费了。
在这里插入图片描述

上图就是面位段再内存中的样子。

联合体

2.1 联合体的概念

联合体也被称为共用体,联合体的成员在内存中是共用一块空间的,所以联合体的大小,至少是最大成员的大小。因为联合体必须有能力保存这个最大成员。

2.2 联合体的共用性

我们先来看看这段代码

#include<stdio.h>
union un1
{
	char ch[3];
	int i;
};

int main()
{
	printf("%d", sizeof(union  un1));
	return 0;
}

再看看它在内存中的大小。在这里插入图片描述
四个字节,这说明 ch和 i 是共用同一块内存空间,这样的话,我们修改了 i ,那么ch 就会发生改变,我们修改了ch,i的值就会被改变。那我们测试一下。我们不给ch初始化,我们把i的值赋值成0x11223344,然后输出ch的所有值。

在这里插入图片描述
因为当前存储模式是小端存储,所以低字节序会存放于低地址处,而数组的地址是随着下标的增长而增加的。所以 就读到了 44 33 22 ,这也更加的说明,联合体的成员是共用同一块内存空间的。那我们这回不修改i的值,把ch数组的三个值赋值,然后打印i的值。

#include<stdio.h>
union un1
{
	char ch[3];
	int i;
};

int main()
{
	union un1 u;
	u.ch[0] = 0x44;
	u.ch[1] = 0x33;
	u.ch[2] = 0x22;
	printf("%x",u.i);
	
	return 0;
}

在这里插入图片描述
因为 i 是未初始化的,而ch数组只有3个元素,所以只把i的低字节序赋值,高字节序依旧是随机值。

2.3联合体的内存计算

结构体存在内存对齐,那么联合体也存在内存对齐。那我们来看看以下代码。

#include<stdio.h>
union un1
{
	char ch[5];
	int i;
};

int main()
{

	printf("%d",sizeof(union un1));
	return 0;
}

那我们来计算一下它的内存大小
首先,char ch 是char类型,它的对齐数是1,所以从偏移量为0的位置开始。
在这里插入图片描述
因为这时联合体,所以 i 也是存放于0的位置
在这里插入图片描述
但是i是4个字节,vs默认对齐数是8,所以i的对齐数是4,ch是char类型,对齐数是1,那么联合体的总大小应该为最大对齐数4的整数倍,而此时联合体的大小是5个字节,不是4的整数倍,所以会浪费下面3个字节的空间。
在这里插入图片描述
所以这个联合体的的大小应该为8。
在这里插入图片描述

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值