【18. 自定义类型:结构体类型】

文章目录

  • 一、结构体类型的声明
    • 1.1 结构体回顾
      • 1.1.1 结构的声明
      • 1.1.2 结构体变量的创建和初始化
    • 1.2 结构的特殊声明
    • 1.3 结构的⾃引⽤
  • 2. 结构体变量的创建和初始化
    • 2.1 对⻬规则
    • 2.2 为什么存在内存对⻬?
    • 2.3 修改默认对⻬数
  • 3. 结构成员访问操作符
    • 3. 结构体传参
  • 4. 结构体内存对⻬
  • 5. 结构体传参
  • 6. 结构体实现位段

一、结构体类型的声明

1.1 结构体回顾

结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.1.1 结构的声明

struct tag
{member-list;
}variable-list;

例如描述⼀个学⽣:

struct Stu
{char name[20];//名字 int age;      //年龄 char sex[5];  //性别char id[20];  //学号 
}; //分号不能丢

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

#include <stdio.h>struct Stu
{char name[20];//名字 int age;      //年龄 char sex[5];  //性别 char id[20];  //学号 
};int main()
{//按照结构体成员的顺序初始化 struct Stu s = { "张三", 20, "男", "2023211270" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id  : %s\n", s.id);//按照指定的顺序初始化 struct Stu s2 = { .age = 21, .name = "thm", .id = "2023211270", .sex = "⼥" };printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id  : %s\n", s2.id);return 0;
}

1.2 结构的特殊声明

在声明结构的时候,可以不完全的声明。
⽐如:

//匿名结构体类型 
struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], *p;

上⾯的两个结构在声明的时候省略掉了结构体标签tag。
那么问题来了?

//在上面代码的基础上,下面的代码合法吗? 
p = &x;

警告:
编译器会把上⾯的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。

1.3 结构的⾃引⽤

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?
⽐如,定义⼀个链表的节点:

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

上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不⾏的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤
⼩就会⽆穷的⼤,是不合理的。
正确的⾃引⽤⽅式:

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. 结构体变量的创建和初始化

我们已经掌握了结构体的基本使⽤了。
现在我们深⼊讨论⼀个问题:计算结构体的⼤⼩。
这也是⼀个特别热⻔的考点: 结构体内存对⻬

2.1 对⻬规则

⾸先得掌握结构体的对⻬规则:

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

//练习1 struct S1{char c1;int i;char c2;
};printf("%d\n", sizeof(struct S1));//练习2 struct S2{char c1;char c2;int i;
};printf("%d\n", sizeof(struct S2));//练习3 struct S3{double d;char c;int i;
};printf("%d\n", sizeof(struct S3));//练习4-结构体嵌套问题 struct S4{char c1;struct S3 s3;double d;
};printf("%d\n", sizeof(struct S4));

2.2 为什么存在内存对⻬?

⼤部分的参考资料都是这样说的:

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

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

让占⽤空间⼩的成员尽量集中在⼀起

//例如: struct S1{char c1;int i;char c2;
};struct S2{char c1;char c2;int i;
};

S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。

2.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;
}

结构体在对⻬⽅式不合适的时候,我们可以⾃⼰更改默认对⻬数。

3. 结构成员访问操作符

3. 结构体传参

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

上⾯的 print1 和 print2 函数哪个好些?
答案是:⾸选print2函数。
原因:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递⼀个结构体对象的时候,结构体过⼤,参数压栈的的系统开销⽐较⼤,所以会导致性能的下
降。

结论:
结构体传参的时候,要传结构体的地址。

4. 结构体内存对⻬

结构体讲完就得讲讲结构体实现 位段 的能⼒。
4.1 什么是位段

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

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

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

⽐如:

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

A就是⼀个位段类型。
那位段A所占内存的⼤⼩是多少?

printf("%d\n", sizeof(struct A));

4.2 位段的内存分配

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

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

  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。

5. 结构体传参

6. 结构体实现位段

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

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

相关文章

疯狂原始人

With every sun comes a new day. 每每旭日东升 A new beginning. 都是崭新的开始 A hope that things will be better today than they were yesterday. 总是期冀今日之美远胜昨日 But not for me. My names Eep. 但不属于我 我叫小伊 And this is my family. The Crood…

【D3.js in Action 3 精译_043】5.1 饼图和环形图的创建(三):圆弧的绘制

当前内容所在位置&#xff1a; 第五章 饼图布局与堆叠布局 ✔️ 5.1 饼图和环形图的创建 ✔️ 5.1.1 准备阶段&#xff08;一&#xff09;5.1.2 饼图布局生成器&#xff08;二&#xff09;5.1.3 圆弧的绘制&#xff08;三&#xff09; ✔️5.1.4 数据标签的添加&#xff08;四&…

【C++】LeetCode:LCR 022. 环形链表 II

题目&#xff1a; 给定一个链表&#xff0c;返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链表尾连接到链表中的位…

Windows环境中Python脚本开机自启动及其监控自启动

1 开机自启动 Windows 10/Windows Server 201X具有一个名为“启动”的已知文件夹&#xff0c;系统每次启动开始自动运行应用程序、快捷方式和脚本时都会检查该文件夹&#xff0c;而无需额外配置。 要在Windows启动时运行脚本&#xff0c;先使用WindowsR快捷键打开“运行”对话…

【QNX+Android虚拟化方案】132 - QNX 系统内存、CPU负载监控

【QNX+Android虚拟化方案】132 - QNX 系统内存、CPU负载监控 1. 获取 showmem 信息2. 获取 thermal adc sensor 信息3. 获取 CPU Load负载信息4. 获取 CPU Freq 频率信息5. 获取 系统开机时间 uptime基于原生纯净代码,自学总结 纯技术分享,不会也不敢涉项目、不泄密、不传播代…

基于JavaSwing的贪吃蛇项目(最新项目)

Java贪吃蛇游戏 目录 文章目录 Java贪吃蛇游戏目录第一章 项目概述1.1 设计背景1.2 设计目的1.3 开发环境 第二章 需求分析2.1 功能需求2.1.1 基础功能2.1.2 扩展功能 2.2 性能需求2.3 用户体验需求 第三章 概要设计3.1 系统架构3.1.1 总体架构3.1.2 类设计 3.2 核心算法设计3…

SpringBoot 赋能:精铸超稳会员制医疗预约系统,夯实就医数据根基

1绪论 1.1开发背景 传统的管理方式都在使用手工记录的方式进行记录&#xff0c;这种方式耗时&#xff0c;而且对于信息量比较大的情况想要快速查找某一信息非常慢&#xff0c;对于会员制医疗预约服务信息的统计获取比较繁琐&#xff0c;随着网络技术的发展&#xff0c;采用电脑…

golang 协程泄漏、协程退出时机、main函数

父协程中生成子协程 问题&#xff1a;如果在一个父goroutine中生成了子goroutine&#xff0c;请问如果父goroutine先执行完毕&#xff0c;那么子协程会自动退出吗&#xff1f; 答案是&#xff1a;不会 先给出结论&#xff1a; 主协程执行完毕后&#xff0c;子协程会继续执行&a…

基于FPGA的智能电子密码指纹锁(开源全免)

基于FPGA的智能电子密码指纹锁 一、功能描述硬件资源需求 二、整体框架知识准备AS608指纹模块4*4数字键盘模块 三、Verilog代码实现以及仿真验证1.AS608_data模块2.check_hand模块3.four_four_key模块4.check_mima模块5.change_mima模块6.seg_ctrl模块7.uart_top模块8.key_debo…

动态计算加载图片

学习啦 别名路径&#xff1a;①npm install path --save-dev②配置 // vite.config,js import { defineConfig } from vite import vue from vitejs/plugin-vueimport { viteStaticCopy } from vite-plugin-static-copy import path from path export default defineConfig({re…

精确的单向延迟测量:使用普通硬件和软件

论文标题&#xff1a;Precise One-way Delay Measurement with Common Hardware and Software&#xff08;精确的单向延迟测量&#xff1a;使用普通硬件和软件&#xff09; 作者信息&#xff1a;Maciej Muehleisen 和 Mazen Abdel Latif&#xff0c;来自Ericsson Research Eri…

基于Java+Swing+Mysql的网络聊天室

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…

Linux-音频应用编程

ALPHA I.MX6U 开发板支持音频&#xff0c;板上搭载了音频编解码芯片 WM8960&#xff0c;支持播放以及录音功能&#xff01;本章我们来学习 Linux 下的音频应用编程&#xff0c;音频应用编程相比于前面几个章节所介绍的内容、其难度有所上升&#xff0c;但是笔者仅向大家介绍 Li…

vue3 项目搭建-9-通过 router 在跳转页面时传参

第一步&#xff0c;在跳转链接处挂载方法&#xff0c;将要传输的数据传入&#xff1a; <a href"#" click.prevent"goToArticle(obj.id)" class"click"><h1>{{obj.title}}</h1><p>作者&#xff1a;{{obj.author}}</p&…

【RBF SBN READ】hadoop社区基于RBF的SBN READ请求流转

读写分离功能的背景及架构 当前联邦生产集群的各个子集群只有Active NameNode在工作,当读写任务变得繁忙的时候,只有一个Active负责处理的话,此时集群的响应和处理能力业务侧感知会明显下降,为此,我们将引入Observer架构,实现读写功能的分离,使得Active只负责写请求,而…

利用R包QstFstComp包进行Qst-Fst分析

1.Qst-Fst分析 安装和加载QstFstComp包 首先&#xff0c;你需要安装devtools包&#xff0c;如果尚未安装&#xff0c;可以使用以下命令安装&#xff1a; install.packages("devtools") 2. 然后&#xff0c;使用devtools安装QstFstComp包&#xff1a;R library(de…

视频自学笔记

一、视频技术基本框架 二、视频信号分类 2.1信号形式 2.1.1模拟视频 模拟视频是指由连续的模拟信号组成的视频图像&#xff0c;以前所接触的电影、电视都是模拟信号&#xff0c;之所以将它们称为模拟信号&#xff0c;是因为它们模拟了表示声音、图像信息的物理量。摄像机是获…

操作系统——大容量存储结构

笔记内容及图片整理自XJTUSE “操作系统” 课程ppt&#xff0c;仅供学习交流使用&#xff0c;谢谢。 大容量存储结构概述 磁盘 磁盘为现代计算机系统提供大量外存。每个盘片为平的圆状&#xff08;类似CD&#xff09;&#xff0c;普通盘片直径为4.5~9.0厘米。盘片的两面都涂着…

Python 正则表达式常用特殊字符及其含义

以下是 Python 正则表达式常用特殊字符及其含义 的全面整理&#xff0c;涵盖了常见和重要的正则符号&#xff0c;以及它们的示例&#xff0c;适合用来写博客或学习使用&#xff1a; Python 正则表达式常用特殊字符及其含义 1. . (点号) 含义&#xff1a;匹配除换行符 \n 以外…

子类调用父类同名方法和属性

1、全量代码 class Master:def __init__(self):self.kongfu [古法煎饼果子配方]print(fMaster_self:{self})def make_cake(self):print(f运用{self.kongfu}制作煎饼果子)class School():def __init__(self):self.kongfu [学校煎饼果子配方]print(fSchool_self:{self})def m…