【C语言__结构体__复习篇5】

目录

前言

 一、结构体基础知识

        1.1 结构体的语法形式

        1.2 创建结构体变量

        1.3 结构体变量的初始化

        1.4 点(.)操作符和箭头(->)操作符

二、匿名结构体

三、结构体自引用 

四、结构体内存对齐

        4.1 内存对齐的规则

        4.2 出现结构体内存对齐的原因

        4.3 修改默认对齐数

五、结构体传参

六、结构体实现位段

        6.1 什么是位段

        6.2 位段成员的内存分配

        6.3 位段的跨平台问题

        6.4 位段使用的注意事项


前言

本篇主要讨论以下问题:

结构体基础知识:

1. 结构体是用来做什么的,结构体的语法形式是怎样的

2. 如何创建一个结构体类型,结构体的全局变量和局部变量如何创建

3. 如何初始化结构体变量,如何自定义顺序初始化结构体变量,结构体中结构体如何初始化

4. 如何找到结构体变量的成员变量

匿名结构体:

5. 匿名结构体如何创建,它有什么需要注意的点

结构体自引用:

6. 结构体如何自引用,结构体自引用时有什么需要注意的点

结构体内存对齐:

7. 结构体内存对齐的规则有哪几点,怎样计算结构体成员变量相对于默认起始地址的偏移

8. 为什么会出现结构体内存对齐,如何改变结构体的默认对齐数

结构体传参:

9. 结构体传参采用传值调用还是传址调用好,为什么

结构体实现位段:

10. 结构体如何实现位段,用结构体实现位段有什么优点和缺点

11. 用结构体实现位段,位段的成员在内存中如何开辟空间的

12. 我们在使用位段时有什么注意事项,采用位段的示例

 一、结构体基础知识

1. 结构体是用来描述复杂对象的,例如,当我们想要描述学生、商品、书籍等自身包含多信息的复杂对象时,就可以创建结构体类型,再去创建结构体变量。

1.1 结构体的语法形式

1. 语法形式:struct tag

                      {

                              成员列表...

                      }变量列表;

① struct是结构体关键字,tag是结构体标签名,struct tag整体表示这个结构体类型的名称。

② { }内的成员列表,用于表示这个结构体类型中有哪些类型的变量(即,复杂对象包含哪些信息),每个成员变量可以是不同类型的变量,甚至是其他结构体,注意!这些成员变量不需要初始化。

③ 变量列表处创建的变量是全局变量,在此处我们可以一次性创建多个全局变量。

2. 创建结构体类型的举例:

    struct stu

    {

            char name[20];

            int age;

            int id[10];

    };

1.2 创建结构体变量

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1, s2;//全局变量

int main()
{
	struct Stu s3;//局部变量
	struct Stu s4;//局部变量
	return 0;
}

1.3 结构体变量的初始化

1. 按结构体成员的顺序初始化结构体变量,用{ }像数组一样直接初始化即可。

2. 自定义顺序初始化结构体变量,采用(.)找到结构体成员名,再赋值即可。

3. 结构体中的结构体的初始话也是用{ }, 类似于二维数组中一维数组的初始化。

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1 = { "张三", 18, "10023211" }, s2 = {"翠花", 19, "10023245"};

int main()
{
	struct Stu s3 = {"lisi", 17, "10023233"};
	struct Stu s4 = {.age = 16, .id = "10012323", .name = "kiki"}; //自定义顺序
	return 0;
}
struct Point
{
	int x;
	int y;
};

struct test
{
	float score;
	struct Point k;
};

int main()
{
	struct test t1 = { 90.8f, {2, 4} };//结构体中的结构体成员初始化
	struct test t2 = {.k.y = 4, .k.x = 9, .score = 89.7f};//自定义顺序
	return 0;
}

1.4 点(.)操作符和箭头(->)操作符

1. 点操作符:结构体变量名.成员名

2. 箭头操作符:结构体指针->成员名(表示通过地址找到它所指向的结构体变量的某个成员)

#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1 = { "张三", 18, "10023211" }, s2 = {"翠花", 19, "10023245"};

int main()
{
	struct Stu s3 = {"lisi", 17, "10023233"};
	struct Stu s4 = {.age = 16, .id = "10012323", .name = "kiki"}; //自定义顺序

	struct Stu* ps3 = &s3;
	struct Stu* ps4 = &s4;
	printf("%s %d %s\n", s1.name, s1.age, s1.id);
	printf("%s %d %s\n", s2.name, s2.age, s2.id);
	printf("%s %d %s\n", ps3->name, ps3->age, ps3->id);
	printf("%s %d %s\n", ps4->name, ps4->age, ps4->id);
	return 0;
}

 

二、匿名结构体

1. 匿名结构体,即结构体类型在定义时 tag 不写。

2. 匿名结构体的特点,可以定义多个全局变量,但不可以定义局部变量。(如果对这个匿名结构体用typedef重命名后,是可以定义局部变量的)

3. 定义两个完全相同的匿名结构体类型,会被编译器认为是不同的结构体类型。

struct
{
	int num1;
	float num2;
};//匿名结构体

三、结构体自引用 

1. 结构体自引用,即在结构体类型定义时,结构体成员变量中存在本结构体类型的指针变量。

2. 匿名结构体不能自引用,这样写的代码可读性很差。

3. 一般在结构体自引用时,会先定义结构体,再用typedef对结构体重命名,注意!重命名结构体后不需要去更改自引用指针变量的类型名称,否则会出现未定义先使用的错误。

typedef struct Stu
{
	char name[20];
	struct Stu* ps;
}Stu;

typedef struct Stu
{
	char name[20];
	Stu* ps;//err,先使用后定义的错误
}Stu;

四、结构体内存对齐

1. 结构体内存对齐==结构体大小如何计算。

4.1 内存对齐的规则

1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处。

2. 其他成员变量要对⻬到对⻬数的整数倍的偏移地址处。

    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

    - VS 中默认的值为 8

    - Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍的偏移地址处,结构体的整体⼤⼩是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

补充:计算结构体成员变量相对于默认起始地址的偏移量用到的宏:offsetof(type, member),type是结构体类型,member是结构体成员名,头文件<stddef.h>,计算结果为size_t类型。

 

 4.2 出现结构体内存对齐的原因

1. 平台原因 (移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

补充:在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:让占⽤空间⼩的成员尽量集中在⼀起

4.3 修改默认对齐数

1. 如果想修改默认对齐数,在定义结构体类型时利用一对 #pragma pack() 这个预处理指令即可,示例如图中的代码。

2. 修改的默认对齐数不要用除1之外的奇数,因为类型的大小通常为偶数。(通常会将默认对齐数设置成1)

#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;
}

五、结构体传参

1. 直接传结构体变量名,函数形参用结构体变量接收。

2. 传结构体变量地址,函数形参用结构体变量指针接收。

3. 结构体传参采用传结构体变量地址好,因为函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销,如果传递⼀个结构体对象所需开辟的内存空间过⼤,会导致参数压栈的系统开销⽐较⼤,代码性能也会下降。

六、结构体实现位段

6.1 什么是位段

位段的声明和结构是类似的,有两个不同:

1. 位段的成员必须是 int、unsigned int 或 signed int ,在C99中位段成员的类型也可以选择其他类型。

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

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

 6.2 位段成员的内存分配

1. 位段的成员可以是 int、unsigned int、signed int 或者是 char 等类型。

2. 位段的内存空间上是按照需要以一次开辟4个字节( int )或者1个字节( char )的⽅式来申请内存空间的。

3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。(要想知道位段成员在内存中如何分配空间的,需要针对不同的平台去研究)

6.3 位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。

2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题)

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这也是不确定的。

总结: 跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

6.4 位段使用的注意事项

1. 位段的⼏个成员可能共用同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置。内存中每个字节有唯一的⼀个地址,⼀个字节内部的bit位是没有地址的。 所以不能对位段的成员使⽤&操作符,这样也就意味着不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

2. 采用位段的示例:⽹络协议中的IP数据报。

struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

int main()
{
	struct A sa = { 0 };
	//scanf("%d", &(sa.b));//这是错误的

	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa.b = b;
	return 0;
}

本篇文章已完结,谢谢支持!!! 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript高级

一、JavaScript 面向对象 面向对象编程介绍ES6 中的类和对象类的继承面向对象案例 1. 面向对象编程介绍 1.1 两大编程思想 面向过程面向对象 1.2 面向过程编程 POP(Process-oriented programming) 面向过程 就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步…

shell脚本编程的例子(50例子)-1

前言 为了提高教学质量&#xff0c;并且能够让童鞋们更好的理解和运用shell脚本以及相关编程&#xff0c;特编写了50个shell例子&#xff0c;目前还在整理过程ing&#xff0c;计划分三期完成。请有需要的同学收藏。后续会申请VIP阅读。…… ^.^ …… ^…^ 实验环境&#xff1…

javaWeb项目-智慧餐厅点餐管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JavaScript Java…

C语言进阶课程学习记录-内存操作经典问题分析

C语言进阶课程学习记录-内存操作经典问题分析 实验-示例1优化 实验-示例2优化 实验-示例3实验-示例4小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 实验-示例1 #include <stdio.h> #include…

ChatGPT研究论文提示词集合1-【主题选择与问题研究、文献综述】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 目录 1.主题选择与问题定义 2.文献综述 3.书籍介绍 AIPaperPass智能论文写作平台 近期小编按照学术论文的流程&#xff0c;精心准备一套学术研究各个流程的提示词集合。总共14个步骤…

HTTP请求中的cookie与session(servlet实现登录页面的表单验证)

一、cookie 与 session 1&#xff09;cookie 与 session 的定义 2&#xff09;相关的servlet中的 方法 二、代码实现 登录页面 1&#xff09;先用 vscode 编写登录页面 注意文件的路径 在webapp路径下 <!DOCTYPE html> <html lang"en"><head>&…

ai写作强大,ai写作哪个软件最好用?

在当今数字化时代&#xff0c;ai技术的发展正以惊人的速度改变着我们的生活和工作方式。其中&#xff0c;ai写作作为一项令人瞩目的创新&#xff0c;展示了强大的文本生成能力。然而&#xff0c;随着各种ai写作软件的涌现&#xff0c;人们不禁困惑&#xff1a;哪个软件才是最好…

【网络设备巡检命令】--思科、华为、H3C、锐捷

【网络设备巡检命令】--思科、华为、H3C、锐捷 一、思科二、华为三、H3C四、锐捷 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 一、思科 1、查看系统信息&#xff1a; show version2、查看时间&#xff1a; show clock3、查看序列号&a…

Zed 捕获图像+测距

Zed 捕获图像测距 1. 导入相关库2. 相机初始化设置3. 获取中心点深度数据4. 计算中心点深度值5. 完整代码5. 实验效果 此代码基于官方代码基础上进行改写&#xff0c;主要是获取zed相机深度画面中心点的深度值&#xff0c;为yolo测距打基础。 1. 导入相关库 import pyzed.sl …

C语言 ─── 操作符详解

目录 1. 算术操作符 2. 移位操作符 2.1 左移操作符 2.2 右移操作符 3. 位操作符 4. 复合赋值符 5. 单目操作符 6. 逗号表达式 7. 隐式类型转换 7.1 整型提升的意义&#xff1a; 7.2 如何进行整体提升呢&#xff1f; 8. 算术转换 ★★★数组名 1. 算术操作符 -…

redis与etcd的对比

1.redis是一种高级的key&#xff1a;value存储系统&#xff0c;其中value支持五种数据类型&#xff1a; 1.1 字符串&#xff08;strings&#xff09; 1.2 字符串列表&#xff08;lists&#xff09; 1.3 字符串集合&#xff08;sets&#xff09; 1.4 有序字符串集合&#xff08;…

管理 nodejs 版本工具 nvm

nvm 方便切换不同版本的 node 及 对应的 npm 版本 一、安装nvm nvm官网 &#xff08;内含下载的文件&#xff0c;点击进去下载&#xff0c;并按照 网站文档步骤 操作即可&#xff09; 二、nvm 基础命令 nvm arch&#xff1a;显示node是运行在32位还是64位。nvm install <…

centos修改启动项加载不同内核

一.背景&#xff1a; 虚拟机中有时需要编译好几个内核版本&#xff0c;make install后系统存在几个内核版本。需要再哪个内核上开发调试就启动特定的内核版本。这就需要修改启动时的内核版本&#xff0c;再物理机或虚拟机启动时可以上下键选择。但有时是docket云环境中或远程时…

CANoe中LIN工程主节点的配置(如何切换调度表)

1&#xff1a;前置条件 1&#xff09;工程已经建立&#xff0c;simulation窗口已经配置好&#xff08;包括且不限于通道mappin好&#xff0c;数据库文件已经添加&#xff09; 2&#xff09;我已系统自带sampleCfg工程&#xff0c;作为例子。如下图 2 &#xff1a;主节点的配置…

前端css中table表格的属性使用

前端css中table表格的属性使用 一、前言二、常见的表格属性1.边框的样式2.布局和对齐3.间距和填充4.背景和颜色5.字体的样式6.边框的圆角 三、简单的表格&#xff0c;例子11.源码12.源码1效果截图 四、给表格添加动画效果&#xff0c;例子21.源码22.源码2的运行效果 五、结语六…

【热门话题】探索与心得:深入体验Microsoft Edge浏览器

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 探索与心得&#xff1a;深入体验Microsoft Edge浏览器一、Edge浏览器概述1.1 发…

通快霍廷格TruPlasma MF中频电源培训PPT课件内容下图

通快霍廷格TruPlasma MF中频电源培训PPT课件内容下图

美业连锁门店收银系统源码-如何查看收款门店对应的加盟商?

美业管理系统源码 博弈美业SaaS系统 连锁多门店美业收银系统源码 多门店管理 / 会员管理 / 预约管理 / 排班管理 / 商品管理 / 促销活动 PC管理后台、手机APP、iPad APP、微信小程序 第一步&#xff1a; 登录pc管理后端 第二步&#xff1a; 进入企业组织管理-门店管理&a…

【Linux】认识文件(一):文件标识符

【Linux】认识文件&#xff08;一&#xff09;&#xff1a;文件标识符 一.什么是文件&#xff1f;1.文件的本质2.文件的分类 二.访问文件操作1.C语言中的访问文件接口i.fopenii.fcloseiii.fwrite 2.系统访问文件接口i.openii.closeiii.write 三.文件管理1.对所有打开文件的管理…

Linux入门学习 之 基础操作指令讲解(小白必看)

股票的规律找到了&#xff0c;不是涨就是跌 一、Linux下基本指令 1.ls 指令 2.pwd 命令 3.cd 指令 4.touch 指令 5.mkdir 指令 6.rmdir指令 && rm 指令 7.man 指令 8.cp 指令 9.mv指令 10.cat 11.more 指令 12.less 指令 13.head 指令 14.tail 指令 15…