C语言 | 深入认识结构体【必收藏】

一、初识结构体

1.1 结构体的声明

struct tag//结构体标签
{
	member_list;//成员列表
}variable_list;//结构体变量

1.2 结构体的创建

举个例子,用结构体描述一个学生信息:

struct Stu
{
	char name[20];//名字
	int age;//年龄
	char sex[10];//性别
	int id;//学号
};

1.3 结构体的初始化

int main()
{
	struct Stu S = { "xiaoming",18,"male",123456 };
	printf("%s\n", S.name);
	printf("%d\n", S.age);
	printf("%s\n", S.sex);
	printf("%d\n", S.id);
	return 0;
}

打印出的结果:
在这里插入图片描述

1.4 结构体的特殊声明

声明结构体时,可以不完全声明,也就是匿名结构体
比如:

struct
{
	int a;
	char b;
	float c;
}x;
struct
{
	int a;
	char b;
	float c;
}*p;

这种代码没有结构体标签,且只能使用一次,比如在这里已经创建了这个结构体类型的变量 x ,后续则再也无法创建这种结构体类型的变量了,因为它是匿名的。

有了上述代码,那么下面代码正确吗?

int main()
{
	p = &x;
	return 0;
}

答案是不正确。
运行结果:
在这里插入图片描述
原因:因为结构体没有名字,所以编译器会认为 x 和 *p 的结构体声明是两个不同的类型,所以无法将 x 的地址给到 p ,这种操作是不允许的。

1.5 结构体的自引用

在一个结构体中包含一个类型为这个结构体本身的成员,比如一个链表结点:

struct Node
{
	int data;         //数据域,用来存储数据
	struct Node* next;//指针域,用来存储下一个结点的地址
};

typedef 重命名结构体,下面的代码证明确吗?

typedef struct
{
	int data;
	Node* next;
}Node;

答案是不正确,因为 Node 是整个结构体创建之后进行重命名的,在结构体成员内部不允许提前试用 Node 来创建成员变量。

如何避免这种情况呢?

要避免这种情况,就尽量不要使用匿名结构体类型。

//链表结点
typedef struct Node
{
	int data;         //数据域,用来存储数据
	struct Node* next;//指针域,用来存储下一个结点的地址
}Node;

二、结构体内存对齐

2.1 对齐规则

  1. 结构体的第一个成员对齐到结构体变量起始位置偏移量为0的地方;
  2. 其他成员对齐到某个数字(对齐数)的整数倍处;
    对齐数 = 编译器的默认对齐数和该成员变量自身大小的较小值
    VS 的默认对齐数是 8
    gcc 没有默认对齐数,对齐数就是成员自身大小
  3. 结构体的总大小为结构体成员最大对齐数的整数倍;
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体中成员的对齐数)的整数倍。

2.2 代码案例(结构体大小的计算)

以下代码均在 VS 中运行
案例1:

struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	return 0;
}

在这里插入图片描述
图示:
在这里插入图片描述
所以,该结构体的大小就是 12 byte

案例二:嵌套的结构体

struct S1//12
{
	char c1;//1 8 1
	int i;  //4 8 4
	char c2;//1 8 1
};
struct S2
{
	char c1;     //1  8  1
	struct S1 s1;//12 S1中的最大对齐数是 4 ,所以S2对齐到 4 的倍数即可
	double d;    //8  8  8
};
int main()
{
	printf("%zd\n", sizeof(struct S2));//24
	return 0;
}

图示:

在这里插入图片描述
所以,该结构体的大小就是 24 byte

2.3 为什么要内存对齐?

  1. 简单来说,结构体的内存对齐就是拿空间来换取时间的做法
  2. 如果我们既要满足对齐,又要节省空间,就要让占用空间小的成员尽量集中在一起

三、结构体的位段

3.1 什么是位段?

形如:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
  1. 位段的成员必须是intunsigned intsigned intchar ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有一个冒号和一个数字。

注意:位段是不跨平台的

3.2 位段的内存分配

IDE : VS2022

代码:

struct S
{
	char a : 3;//bit
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

char 是 1 byte ,等于 8 bit;
按照从右向左使用空间,空间不够则再分配一块空间;

画图表示:
在这里插入图片描述
最后的 2 进制序列为:

01100010 00000011 00000100

转换成 16 进制:

62 03 04

上述分析是否正确?进入调试看一下

在这里插入图片描述
可以看到分析是正确的。

看完有所收获的话就留个赞叭!!!嘿嘿 ~
在这里插入图片描述

  • 35
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值