当前位置: 首页 > news >正文

结构体详解

🍍个人主页🍍:🔜勇敢的小牛儿🚩

🔱推荐专栏🔱:C语言知识点

⚠️座右铭⚠️:敢于尝试才有机会

🐒今日鸡汤🐒:有时候躺下是为了更好的休息。

思维导图:

目录

 一,结构体的基础知识

1.1结构体类型的简单介绍:

1.2结构体类型的声明:

 1.3结构体变量的定义:

1.4结构体的初始化:

1.5结构体传参:两种方式

二,结构体的自引用

2.1: 在结构体内部放一个相同的结构体?

2.2:在结构体内部放一个结构体类型的指针? 

上面两种写法中,第一种写法是错误的。第一种写法就像一个递归但是没有终止条件,所以会无限循环下去。

三,结构体的内存对齐(求结构体大小)

3.1:练习

3.2:结构体节省空间的操作:

 比如,第一个结构体变量s1的大小本来是12的,现在我们进行下列操作:

3.3:结构体内存对齐的原因:以空间换时间

4.结构体的位段

4.1:什么是位段:位就是二进制位。

4.2:位段内存的分配:

 结束


 一,结构体的基础知识

1.1结构体类型的简单介绍:

结构体是一些值的集合,这些值叫做结构体的成员。每一个成员可以是不同类型的变量。

1.2结构体类型的声明:

1.2.1,声明的通式:

struct tag// 类型为struct tag
{
member - list ;/ /结构体成员
} variable - list ;// 变量名

例子:

struct Stu {//定义一个结构体类型来接收一个学生的个人信息
            //strut Stu 是一个类型
	char name[20];
	char ID[12];
	int age;
	float weight;
};

 1.3结构体变量的定义:

1.3.1:边声明边定义:定义一个全局结构体变量s1

struct Stu {
	char name[20];
	char ID[12];
	int age;
	float weight;
}s1;//定义了一个全局的结构体变量s1 

1.3.2:先声明再定义:定义一个全局结构体变量s2 

struct Stu {
	char name[20];
	char ID[12];
	int age;
	float weight;
};
struct Stu s2;//定义一个结构体全局变量s2

1.3.3:先声明再定义:定义一个局部结构体变量s3

struct Stu {
	char name[20];
	char ID[12];
	int age;
	float weight;
};
int main() {
	struct Stu s3;//在主函数里面定义一个结构体变量,所以是个局部变量
	return 0;
}

1.3.4:使用tepydef关键字重新定义再使用:

typedef struct Stu {
	char name[20];
	char ID[12];
	int age;
	float weight;
}Teacher;//此时将结构体类型struct Stu重命名为Teacher.(Teacher 此时是一个类型而不是变量)
Teacher s1;
int main() {
	 Teacher s2;
	return 0;
}

1.4结构体的初始化:

1.4.1:像数组一样初始化:带个大括号然后按顺序初始化

struct Stu {
	char name[20];
	char ID[12];
	int age;
	float weight;
}s1;
s1 = { "zhang san","1357924",20,45.9f };//初始化

内嵌一个结构体:

struct people {
	char name[20];
	int age;
};
struct Stu {
	char name[20];
	char ID[12];
	struct people s2;
	int age;
	float weight;
};
struct Stu s1 = { "zhang san","1357924",{"lishi",25},20,45.9f};//初始化

1.4.2:使用.操作符,不按顺序初始化.

struct Stu s1 = { .name = "zhang san",.weight = 46.9f,.ID = "1357924",	.s2 = {"lishi",25},.age = 20};//初始化

1.5结构体传参:两种方式

1.5.1:值传参

将结构体变量的所有值传到一个函数里。有明显的缺点:

1.结构体变量的大小可能很大,造成空间的浪费。

2.传过去的值被形参接受以后相当于一份临时拷贝,修改形参的值不能达到修改实参的效果。

1.5.2:址传参

顾名思义传到函数里面的是结构体的地址,址传参可以将形参与实参联系到一起。可以通过改形参来改实参。而且,传址的效率更高。

二,结构体的自引用

结构体的自引用就是自己引用自己,就是在一个结构体内部引用一个相同类型的结构体变量。那结构体应该怎样引用自己呢?

2.1: 在结构体内部放一个相同的结构体?

struct Node {//声明一个结构体
	int data;
	struct Node next;//在结构体内部自引用自己
};

2.2:在结构体内部放一个结构体类型的指针? 

struct Node {//声明一个结构体
	int data;
	struct Node* next;//在结构体内部自引用自己
};

上面两种写法中,第一种写法是错误的。第一种写法就像一个递归但是没有终止条件,所以会无限循环下去。

三,结构体的内存对齐(求结构体大小)

规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

3.1:练习

3.1.1:

#include<stdio.h>
struct S1 {
	char c1;
	int i;
	char c2;
};
int main() {
	printf("%d\n", sizeof(struct S1));
}

输出:12

解释:  

因为i的变量大小为4个字节,所以i的对齐数为4,所以在C1占据一个字节时要浪费3个字节使i对齐到4。因为默认对齐数为8,结构体成员最大的元素为i有4个元素,所以这个结构体的大小要为4的倍数,满足条件的最小的4的倍数为12,所以结构体大小为12个字节。

 

3.1.2:结构体嵌套:

#include<stdio.h>
struct S1 {
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	struct S1 s3;
	double d;
};
int main() {
	printf("%d\n", sizeof(struct S2));
	return 0;
}

输出:24

解释:

4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
#include<stdio.h>
struct S1 {
	char c1;//1
	int i;//4
	char c2;//1
};
struct S2
{
	char c1;//1
	struct S1 s3;//12
	double d;//8
};
int main() {
	printf("%d\n", sizeof(struct S2));
	return 0;
}

 

3.2:结构体节省空间的操作:

让占用空间小的变量聚集在一起 

 比如,第一个结构体变量s1的大小本来是12的,现在我们进行下列操作:
 

#include<stdio.h>
struct S1 {
	char c1;//将c1与c2放在一起
	char c2;
	int i;
};
int main() {
	printf("%d\n", sizeof(struct S1));
}
输出:8

3.3:结构体内存对齐的原因:以空间换时间

1. 平台原因 ( 移植原因 )
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
2. 性能原因
数据结构 ( 尤其是栈 ) 应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。

4.结构体的位段

4.1:什么是位段:位就是二进制位。

这就是位段:

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

位段的声明与结构体相似,但是有两个不同:

1.位段的成员必须是int,unsigned int,signed int型。

2.位段的成员名后边有一个冒号和一个数字。

4.2:位段内存的分配:

位段的内存是按4个字节和1个字节两种方式来分配的。

1.当成员为int类型的时候就按4个字节来分配。

2.当成员为char型(也是int型)时就按一个字节来分配。

 4.2.1:练习:

#include<stdio.h>
struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};
int main() {
	struct A a;
	printf("%d\n", sizeof(a));
	return 0;
}

输出:8

解释:

#include<stdio.h>
struct A
{
	int _a : 2;//2个比特位
	int _b : 5;//5个比特位
	int _c : 10;//10个比特位
	int _d : 30;//30个比特位
};//共47个比特位
int main() {
	struct A a;
	printf("%d\n", sizeof(a));//因为是int型,
                              //所以先分配4个字节的空间也就是32个比特位
                              //但是不够,所以还要分配4个比特位。一共分配了8个比特位。
	return 0;
}

 结束

相关文章:

  • 【数据结构与算法】设计循环队列
  • go语言gin框架学习
  • 一个超简单的渐变平行四边形进度条
  • 安装好unity后给unity配置及插件和资源相关的事情
  • docker版jxTMS使用指南:勾连python
  • 值得记忆的STL常用算法,分分钟摆脱容器调用的困境,以vector为例,其余容器写法类似
  • 重学Java设计模式-结构型模式-组合模式
  • Android11以上版本使用高德定位,定位成功,卫星数一直为0
  • 【十二天学java】day06之方法详解
  • Revit明细表计算重量和明细表导出功能
  • 基于飞桨实现的特定领域知识图谱融合方案:ERNIE-Gram文本匹配算法
  • Python实现二叉树递归遍历
  • 如何在 Vue 中使用 防抖 和 节流
  • 高效实施ClickHouse数据备份
  • 关于qt creator手动添加构建套件kit问题
  • 【进阶数据结构】——红黑树
  • 初识进程
  • Mybatis搭建环境及其原理【搭建Mybatis程序】
  • 一次内存泄露排查
  • 1.linux操作命令
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉