结构体课程自我理解

news2024/9/19 20:20:25

目录

1. 结构体类型的声明

 1.2特殊的声明方法

1.3结构体的自引用 

1.4 typedef 对结构体命名 

2. 结构体变量的创建和初始化

3. 结构成员访问操作符

4. 结构体内存对⻬

4.1下面给大伙4个练习题,有自我解析 

4.2为什么存在内存对齐 

4.3修改默认对齐数 

5. 结构体传参


1. 结构体类型的声明

结构体就是一些值的集合,然后将这些值成为成员变量。成员变量可以是不同的类型的变量

 可以用结构体表示一个学生的数据。(其他物品的数据也可以表示),下面是一个描述学生的结构体。

struct stu
{
	char name[10];
	int age;
	int h;

}s;

 这个结构体的名字叫做stu 类型是struct stu 里面有三个成员变量。并且在下面还创建了一个结构体变量 s 。当然在其他函数下也可以创建变量,int i = 0,创建的是一个整型。

那结构体 的创建就是这样 struct stu yang = {0};下面是对结构体变量进行赋值。

int main()
{
	struct stu s = { "yangjianglong",20,172 };
	printf("%s %d %d",s.name, s.age, s.h);
	return 0;
}

 1.2特殊的声明方法

struct
{
 int a;
 char b;
 float c;
}x;
struct
{
 int a;
 char b;
 float c;
}a[20], *p;

上面这两个代码省去了结构体的名字,上面原始的声明方法的结构体名字就是stu。而下面这两个代码省去了 结构体名字,这样也是可以的,但是这样的结构体就是一次性的,所以你必须在后面就把需要的变量添加上去。就像第一串的 x 变量和 后面的 a【20】和 *p 变量。

虽然这两个变量无名字但是他们的类型还是不同的。

1.3结构体的自引用 

 对于结构体的自引用来说必须用指针,而不能直接赋值变量。下面给到解析

 

如果这样写一个结构体自引用的话,首先他会无限循环下去,然后结构体的大小不能计算。因为这个结构体无限引用无法知道大小。 sizeof(struct Node)是无限大的,会报错。

 但是如果你用指针自引用的话就可以避免这个问题

 

因为指针的大小在32和64位系统上是固定的为 4个或8个字节。 所以这里的在结构体里的自引用是能求出大小的。所以不会出现错误。

1.4 typedef 对结构体命名 

typedef struct stu
{
	int data;
}Node;


typedef 可以用来定义一个类型,这里就是将结构体 struct stu 重命名为Node。

下次使用时 就用 Node 作为类型名即可,就像 Node s = {0};。

思考下面这串代码错在哪里?

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

这个代码采用了 匿名结构体的形式初始化,但Node 是在整个结构体赋值结束后,才有的名称,所以上面的 Node* next 理论上并不存在。这样是不行的。 

解决⽅案如下:定义结构体不要使⽤匿名结构体了 

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

2. 结构体变量的创建和初始化

struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = { x, y };

第一步定义一个结构体,p1 是 在结构体后直接 创建变量,p2 是创建结构体变量但不赋值,p3是 创建结构体变量 并且赋值。 

struct Point
{
	int x;
	int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值。
struct Point p3 = { x, y };
struct Stu //类型声明
{
	char name[15];//名字
	int age; //年龄
};
struct Stu s = { "zhangsan", 20 };//初始化
struct Node
{
	int data;
	struct Point p;
	struct Node* next;
}n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化
struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化

 先创建一个 stu 结构体,s 变量是创建变量 并且赋值为 “zhangsan” ,20.

第二个是Node 结构体,n1 是直接在结构体后嵌套初始化,这样就不用在写一遍结构体类型

n2 是在结构体定义好了后,用类型名 创建一个 变量 并对其赋值。data 赋值为 20,在其中在调用 point 定义一个变量 p 其中在赋值 x y 为 5,6. 给next 循环指针赋值为 NULL 。

3. 结构成员访问操作符

结构体的操作符有两个,第一个是 .     第二个是 ->,第一个是一个点,第二个是一个箭头(减号加大于符号)。

举例如下:

#include <stdio.h>
#include <string.h>
struct Stu
{
	char name[15];//名字
	int age; //年龄
};
void print_stu(struct Stu s)
{
	printf("%s %d\n", s.name, s.age);
}
void set_stu(struct Stu* ps)
{
	strcpy(ps->name, "李四");
	ps->age = 28;
}
int main()
{
	struct Stu s = { "张三", 20 };
	print_stu(s);
	set_stu(&s);
	print_stu(s);
	return 0;
}

 这个代码有 一个结构体定义,两个小函数。

定义一个 stu 结构体 添加两个成员变量。第一个变量作为名字,第二个作为年龄。

第一个函数则是用来打印 定义的变量,第一次打印s.name则是 在main 函数里定义的“张三”。s.age 是在main 函数里定义的 20.

第二个函数用来 改变 s 变量 里的内容。

4. 结构体内存对⻬

这是一个热门的考点

结构体的对齐规则:

1. 结构体的第⼀个成员对⻬到相对结构体变量起始位置偏移量为0的地址处
2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
- VS 中默认的值为 8
- Linux中没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的 整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构 体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

4.1下面给大伙4个练习题,有自我解析 

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

这个代码打印出来后,结果是12.怎么计算的呢。如果按我们正常计算字节数的话上面3个变量的字节加起来就 6 个字节啊?

12是怎么算的?

首先根据对齐规则 char 的字节 为 1 VS的默认对齐字节为8. 所以 char c1先占了 第一个字节。

但 第二个 int i 变量 的字节大小为 4 VS为 8 。所以对齐数为4. 因为对齐数为4,由对齐规则第二点得知 成员变量 要对齐到 整数倍的地址。所以int 的地址应该是从第4个 字节开始。

然后在占用4个字节所以现在 到了 第 8个字节。(与第一个char之间的三个字节算浪费了)

第三个char 是1个 对齐字节 所以不管什么整数都是他的整数。所以他直接占用第九字节即可

那不应该到这里就结束了嘛。但根据对齐规则第三点。结构体的大小必须是 所有成员变量的最大对齐数的整数倍。因为 int 的为最大对齐数 所以要是 4 的整数倍,现在已经占了 9个字节的位置,所以再给 2个字节到达 第12个字节即可。所以 大小为 12字节。

 接下来给三个练习题,前面两个和上面这题没多大区别,重点是知道每个类型的字节大小。

最后一题需要知道嵌套结构体的字节大小。(会有自我理解在下面。)

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

//练习3
struct S3
{
	double d;
	char c;
	int i;
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};

S4的讲解在这!!

对于一个结构体里面在嵌套一个结构体变量这样的结构体大小是这样算的

首先你得先将 S3展开 就是将 S3里的全部变量拿出来,s3 就不是一个整体了,变成下面这样

struct S4
{
	char c1;
	double d;
	char c;
	int i;
	double d;
};

 这样计算出来就为32了。(不懂的uu可以私信我。)

4.2为什么存在内存对齐 

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定
类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要 作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地 址必须是8的倍数。如果我们能保证将所有的double类型的数据的地对⻬成8的倍数,那么就可以 ⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两 个8字节内存块中。 总体来说:结构体的内存对⻬是空间来换取时间的做法。
那在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:
让占⽤空间⼩的成员尽量集中在⼀起
struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};

经过计算肯定是s2的内存占用较少,所以将空间小的成员集中可以节省空间。

4.3修改默认对齐数 

默认对齐数是可以修改的,但要用到 # pragma这个预处理指令。实例看下面: 

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
	char c1;
	int i;
	char c2;
};
#pragma pack()//取消设置的默认对⻬数,还原为默认
int main()
{
	//输出的结果是什么?
	printf("%d\n", sizeof(struct S));
	return 0;
}

5. 结构体传参

struct S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s); //传结构体
	print2(&s); //传地址
	return 0;
}

 上面两种结构体传参都是对的,但我们尽可能的使用 指针传参,为什么呢?

可以想象如果 你传的是一个结构体,如果那个结构体的体量很大,字节数很多,那么在传的时候,进行压栈,就会花费更多的空间和时间。

但如果你只是一个指针,你的字节数只会为4或者8. 那需要的时间 和空间都会相对较小。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1080943.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Centos中利用自带的定时器Crontab_实现mysql数据库自动备份_linux中mysql自动备份脚本---Linux运维工作笔记056

这个经常需要,怕出问题因而需要经常备份数据库,可以利用centos自带的定时器,配合脚本实现自动备份. 1.首先查看一下,这个crontab服务有没有打开: 执行:ntsysv 可以看到已经开机自启动了. 注意这个操作界面,用鼠标不行,需要用,tab按键,直接tab到确定,或取消,然后按回车回到命…

如何下不可选中的文章

背景&#xff1a; 看到了一篇比较有用的微信公众号文章&#xff08;这个文章应该是跳转到了公众号外的网站的 url 了&#xff09;&#xff0c;想留档&#xff0c;但是手机选中不了。但是这个事情作为程序员&#xff0c;怎么能束手呢。 操作&#xff1a; 1、将微信公众号链接在…

Copa:无需重建镜像,直接修补容器漏洞

关注【云原生百宝箱】公众号&#xff0c;与你一起探讨应用迁移&#xff0c;GitOps&#xff0c;二次开发&#xff0c;解决方案&#xff0c;CNCF生态。 copa 是一个使用 Go 编写的 CLI 工具&#xff0c;基于 buildkit&#xff0c;可以根据像 Trivy 这样的流行工具的漏洞扫描结果直…

LeetCode-343-整数拆分

题目描述&#xff1a; 给定一个正整数 n &#xff0c;将其拆分为 k 个 正整数 的和&#xff08; k > 2 &#xff09;&#xff0c;并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 题目链接&#xff1a; LeetCode-343-整数拆分 解题思路&#xff1a; 还是根据动规五…

Centos (含Rocky-Linux) VSFTPD 简单设置

本文并非深入讨论vsftp配置的文章&#xff0c;仅以能连通为目的&#xff0c;适合那些临时需要上传点东西到服务器的场景。 一、安装 dnf -y updatednf -y install vsftpdsystemctl start vsftpdsystemctl enable vsftpd二、防火墙 开放21端口&#xff1a; firewall-cmd --zo…

利用正则表达式进行数据采集和处理

目录 一、正则表达式的概述 二、正则表达式在数据采集中的运用 1、匹配和提取数据 2、数据清洗 3、数据验证 三、Python中的re模块介绍 1、re.match()方法 2、re.search()方法 总结 正则表达式是一种强大的文本处理工具&#xff0c;它可以用于模式匹配、提取、替换等操…

Android 自定义横向时间轴

示例&#xff1a; 一、添加依赖 dependencies {******//添加RecyclerView的依赖包implementation androidx.recyclerview:recyclerview:1.2.1 } 二、页面代码 activity_main.xml: <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmln…

大语言模型迎来重大突破!找到解释神经网络行为方法

前不久&#xff0c;获得亚马逊40亿美元投资的ChatGPT主要竞争对手Anthropic在官网公布了一篇名为《朝向单义性&#xff1a;通过词典学习分解语言模型》的论文&#xff0c;公布了解释经网络行为的方法。 由于神经网络是基于海量数据训练而成&#xff0c;其开发的AI模型可以生成…

Virtual Box + Vagrant 快速搭建 Linux 虚拟开发环境

Virtual Box Vagrant 快速搭建 Linux 虚拟开发环境 1、根据自己所使用的操作系统平台&#xff0c;选择下载对应的虚拟机客户端软件 Virtual Box 并进行安装&#xff0c;这里选择的是 Virtual Box 7.0.10 Windows hosts 平台安装包。 选择安装目录为其他盘&#xff0c;避免默认…

【C++】STL总结:理解六大核心组件、各个组件主要功能

文章目录 六大核心组件的关联性1.容器2.迭代器3.算法4. 仿函数 / 函数对象5. 适配器 / 包装器6. 空间配置器 六大核心组件的关联性 1.容器 &#x1f517;&#x1f449;点击跳转 2.迭代器 &#x1f517;&#x1f449;点击跳转 3.算法 &#x1f517;&#x1f449;点击跳转 …

安科瑞消防设备电源监控系统在城市建筑中的应用

安科瑞 崔丽洁 摘要&#xff1a;2021年十四五规划里&#xff0c;也都有写到关于绿色循环经济的议题。在这样一个背景下&#xff0c;建筑行业也需要为实现“双碳”目标作出改变&#xff0c;建筑行业飞速发展&#xff0c;大量高层建筑拔地而起&#xff0c;在为了响应国家对于建筑…

生态共建 | 7-9月,YashanDB与13款产品完成兼容互认证

7-9月&#xff0c;深圳计算科学研究院崖山数据库系统YashanDB与柏科数据技术&#xff08;深圳&#xff09;股份有限公司、深圳钛铂数据有限公司、华润数字科技有限公司、武汉中地数码科技有限公司、龙芯中科技术股份有限公司、华瑞指数云科技&#xff08;深圳&#xff09;有限公…

LeetCode-199-二叉树的右视图

题目描述&#xff1a; 题目链接&#xff1a;LeetCode-199-二叉树的右视图 解题思路&#xff1a; 在 102 的基础之上进行改进&#xff0c;一维数组每次只保存 size1 时候的值 代码实现&#xff1a; class Solution {public List<Integer> rightSideView(TreeNode root) {i…

【算法-动态规划】斐波那契第 n 项

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

网络初识(JAVA EE)

文章目录 一、网络发展史二、网络通信基础三、协议分层四、封装和分用 一、网络发展史 独立模式&#xff1a;计算机之间相互独立&#xff0c;每个终端都各自持有客户数据&#xff0c;且当处理一个业务时&#xff0c;按照业务流程进行 网络互连&#xff1a;将多台计算机连接在一…

ELK集群 日志中心集群、kafka、logstash

ES&#xff1a;用来日志存储 Logstash:用来日志的搜集&#xff0c;进行日志格式转换并且传送给别人&#xff08;转发&#xff09; Kibana:主要用于日志的展示和分析 kafka Filebeat:搜集文件数据 es-1 本地解析 vi /etc/hosts scp /etc/hosts es-2:/etc/hosts scp /etc…

Maven Web应用

目录 创建 Web 应用 构建 Web 应用 部署 Web 应用 测试 Web 应用 本章节我们将学习如何使用版本控制系统 Maven 来管理一个基于 web 的项目&#xff0c;如何创建、构建、部署以及运行一个 web 应用。 创建 Web 应用 我们可以使用 maven-archetype-webapp 插件来创建一个简…

景区经营系统如何落地运营?景区系统重点功能推荐

在数字经济的引领下&#xff0c;文旅行业也迎来数字化转型的浪潮&#xff0c;需要借助数字化工具完成一整套景区经营管理落地方案。如蚓链数字化文旅系统拥有多端开发能力&#xff0c;可实现PC端、移动端、小程序等多端开发&#xff0c;让游客可随时随地畅游景区&#xff0c;提…

“互联网+”时代,3D全景营销打造千人千面营销点

现如今&#xff0c;元宇宙产业正在加速演化&#xff0c;传统的营销方式已经渐显疲态&#xff0c;3D全景营销的沉浸式体验为众多企业品牌打开新的营销领域。“互联网”时代&#xff0c;一些简单的图片、宣传单已经不能满足商家的宣传需求了&#xff0c;很难对客户产生吸引力&…

服务器编程基本框架

服务器编程基本框架 虽然服务器程序种类繁多&#xff0c;但其基本框架都一样&#xff0c;不同之处在于逻辑处理。 I/O 处理单元是服务器管理客户连接的模块。它通常要完成以下工作&#xff1a;等待并接受新的客户连接&#xff0c;接收客户数据&#xff0c;将服务器响应数据返回…