C语言探索之旅:深入理解结构体的奥秘

目录

引言

一、什么是结构体?

二、结构体类型的声明和初始化

1、结构体的声明

2、结构体的初始化

3、结构体的特殊声明

4、结构体的自引用

5、结构体的重命名

三、结构体的内存对齐

1、对齐规则

2、为什么存在内存对齐?

3、修改默认对齐数

三、结构体传参

四、结构体的位段

1、什么是位段?

2、位段的内存分配

3、位段的跨平台问题

4、注意事项

五、结语


引言

在C语言的世界里,结构体是一项强大的工具,它让我们能够将多种不同类型的数据组合在一起,构建复杂的数据模型。无论是模拟现实中的实体对象,还是组织程序中的多信息数据,结构体都扮演着不可或缺的角色。本文将带你从基础入门,逐步探索结构体的定义、使用方法以及在实际开发中的应用技巧,帮助你更高效地掌握C语言的核心知识。

一、什么是结构体?

首先来回顾一下数组的定义:数组就是相同类型元素的集合。但是,实际中描述某个物体的数据绝大部分都是不同类型,这时候就需要一个新的东西来把这些数据集合起来:结构体

结构体就是存放不同类型的数据的集合。

二、结构体类型的声明和初始化

1、结构体的声明

struct name
{
	int member1;
	double member2;
	char member3;
	//……
};//分号不能丢掉!

struct 是语法要求 ,

name为结构体的名字,自己可以随便起名,

大括号里的 member 为结构体的成员,

最后的分号不能丢掉!

2、结构体的初始化

要想初始化结构体,我们首先要声明一个结构体:

struct student
{
	char name[20]; //学生的姓名
	int mumber;    //学生的学号
	double score;  //学生的平均分
};

如上代码,我创建了一个学生的结构体,包含了姓名,学号和平均分,下面就要创建一个结构体变量:

int main()
{
	struct student n1;
	return 0;
}

n1 就是一个结构体变量,对其初始化跟普通的变量一致:

struct student n1 = {"sichenglang" ,5438438 , 48.38};

3、结构体的特殊声明

有特殊结构体是可以匿名的:

struct 
{
	char name[20]; //学生的姓名
	int mumber;    //学生的学号
	double score;  //学生的平均分
}n1; //创建了一个结构体变量 n1

但是,匿名结构体只能使用一次,即创建一个变量 n1。

那么如果再次创建一个元素相同的匿名变量呢?

struct student
{
	char name[20]; //学生的姓名
	int mumber;    //学生的学号
	double score;  //学生的平均分
}n2;

编译器认为,这两个结构体是两种结构体,如果你要 n1 = n2 ; 那么编译器就会发出警告!

4、结构体的自引用

结构体内不可能嵌套它本身,如同下面的代码:

struct tong
{
   int ma;
   struct tong si;
};

这个代码编译器会发出警告!因为 sizeof(struct tong) = sizeof(struct tong) + sizeof(int)  显然这是不可能的

因此,结构体自引用只能以指针的形式:

struct tong
{
   int a;
   struct tong* si;
};

其实这玩意就是单链表的一个元素

5、结构体的重命名

用 typedef 来给结构体重命名:

typedef struct tong
{
	int si;
	char lang;
}ma;

这里我们可以在结构体声明的时候就顺带重命名了

struct tong n1;
ma n1;

以上两行代码是等价的

三、结构体的内存对齐

先来看看以下代码:

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

struct s2
{
	char c1;
	int n;
	char c2;
};

int main()
{
	printf("%zu\n" ,sizeof(struct s1));
	printf("%zu\n" ,sizeof(struct s2));
	return 0;
}

结果是:

这就是由结构体的内存对齐造成的

1、对齐规则

(1)、第一个成员对齐到结构体变量起始位置偏移量为0的位置

(2)、从第二个成员开始,都要对齐到某个对齐数的整数倍的位置

   对齐数 = 编译器默认对齐数与该成员大小的较小值

   VS的默认对齐数为8 

(3)、结构体总的大小为最大对齐数的整数倍 (含嵌套里面的结构体的对齐数)

下面是结构体s1 和 s2 对齐结构图

最终的大小为4的倍数就是8

而现在,从 0 ~8 共9个字节,我们要求的是最大偏移量 4 的整数倍,那么就只有12了

2、为什么存在内存对齐?

平台原因,性能原因

总的来说,是用空间换取时间的做法

3、修改默认对齐数

用 #pragma 这个指令,具体看下面的例子:

#pragma pack(1) //设置数一般都是2的几次方
struct s1
{
	char c1;
	int n;
	char c2;
};
#pragma pcak() //还原默认对齐数

三、结构体传参

函数传参的时候,参数是需要压栈,会耗费时间和空间,如果传递的结构体过大,那么时间和空间的耗费就比较大,导致性能下降

结论:结构体传参,尽量传地址

 

四、结构体的位段

1、什么是位段?

这里直接举个例子说明:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

冒号后面的数字比啊是这个成员要占用的比特位的数量,比如a只占两个比特位

位段的成员必须是 int ,unsigned int ,signed int 类型

2、位段的内存分配

位段的空间上是按照需要以4个字节(int)或者 1个字节(char)来开辟空间的

例如这个位段:

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
//sizeof(struct S) = 3
它在内存中是这样分配的:

3、位段的跨平台问题

位段中的最大位数目不确定;
位段中成员在内存中从左向右分配还是从右向左分配不确定
结论: 跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

4、注意事项

不能对位段成员取地址,也不能用scanf对位段成员输入,但可以
int b = 0;
scanf("%d" ,&b);
S._b = b;

五、结语

结构体作为C语言中实现复杂数据管理的重要机制,理解并善用它,能够极大地提升你的程序设计水平。从定义到操作,从简单到复杂,逐步掌握结构体的使用技巧,你将在C语言的编程世界中游刃有余。希望这篇文章能为你的学习提供有价值的启示,让你在未来的开发道路上更加自信和高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值