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

结构体(C语言进阶)(一)

目录

前言

1、结构体声明

1.1 结构体基本概念

1.2 结构体声明

1.3 特殊的结构体声明

1.3.1 匿名结构体声明

1.4 结构体自引用

1.5 结构体变量的定义和初始化

1.6 结构体内存对齐

1.7 修改默认对齐数

1.8 结构体传参

总结


前言

        C语言除了有其内置类型,还有自定义类型。因为在实际运用中,我们可能会遇到一类数据,它的内容是由多种数据组成的,例如:一个学生的个人信息,它就包含了姓名、年龄、学号等等。这些数据并不是统一的类型,因此不能用C语言本身的数组去定义,那如果我们需要将其放在一个变量中时,我们就可以运用到结构体了。结构体的出现就是用来处理这样的内容数据不统一的变量,这给了编程者很大的操作空间,使得编程更加灵活自由。

        接下来我们就详细介绍一下结构体吧。

1、结构体声明

1.1 结构体基本概念

        结构体是一些值得集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。类比数组,数组的成员变量的类型必须相同,而结构体没有要求。

1.2 结构体声明

        举个例子:

        

        这是一个“学生”结构体的定义,它的类型为struct stu,struct是结构体声明的前缀,stu是自己定义的类型名,因为是包含“学生”信息的结构体,因此我们用来stu。定义好类型名之后,我们用一个大括号阔住接下来的内容,并且在大括号后面加上“;”。然后我们来看大括号里面的内容,上面这个例子我大括号里面放了了四个变量,分别用来存放:姓名;年龄;性别;学号。如果你想定义其他的也是可以的,内容是可以自己灵活改变的。这样,我们就算是定义好了一个结构体类型,它可以用来表示学生的基本信息。

        注意,上面仅只是建立了一个类型,还没有正真建立结构体变量。相当于我们只是建立了一个类似于:int;float;double这样的东西,后面还需要用具体的变量去承载。类比int,我们定义int类型的变量,还得想一个变量名,比如“p”,那我们定义方式就是:int p=0;那这里p就是一个整型的变量。同理结构体也一样,上面的操作只是相当于写了一个“int”,还需要想一个变量名,假如“p”,定义时像这样:struct stu p={ 0 };这样,才算定义出了一个结构体变量p。

        例如:

        这是两种声明方法,可以在结构体建立时就声明变量p1,也可先定义类型,后再声明p2。这两种方法的区别是,前者定了全局变量,后者定义的是局部变量。

1.3 特殊的结构体声明

1.3.1 匿名结构体声明

        例如:

        声明结构体类型时,没有名字。这种类型的结构体只能用一次,而且是马上建立马上用:

        像这样,定义一个s1的结构体之后,就再也无法使用这个匿名结构体。

        匿名结构体特点:对于不同的匿名结构体,即使其内容一模一样,计算机还是会认为他们不是一个类型,如果用两个内容相同的匿名结构体,一个定义变量,一个定义指针,当指针指向变量时,计算机会报错。

1.4 结构体自引用

        结构体内部包含自己。看一个例子:

        对于这个结构体,第一个数据是一个整型,第二个数据定义的居然是指向自己的指针类型,这样的话,我们去访问这个结构体时,可以在它内部发现指向自己的指针,这就是结构体的自引用。

        想一个问题:我们能不能在结构体里面直接定义一个本身类型的结构体变量呢?我们为什么要定义指针呢?

        原因:因为如果我们在一个结构体里面直接定义它本身类型的变量,那我们将无法预估这个结构体的大小,因为每次计算这个结构体第二个参数的大小时,又会进入到一个结构体里面,而那个结构体的第二个参数还是结构体,是一个死循环。所以我们在结构体自引用时,只定义自身类型 的指针,就能避开这个问题。

        为什么要这么用?

        这个用法很重要,有了这样一个功能,才能实现链表建立。是数据结构很重要的内容。

        注意:结构体的自定义是无法通过匿名结构体实现的。

1.5 结构体变量的定义和初始化

        这个在前面其实已经讲到一部分,下面看几种定义的例子:

        p1和p2定义时未初始化,p3定义时进行了初始化。这三种方式都定义了结构体变量,p3在定义时进行了初始化。

1.6 结构体内存对齐

        先看一个例子,思考如下结构体占用几个字节:

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

        答案:

        答案是12。那么这是为什么呢?为什么不是6?这就涉及到我们揭晓来要讲的:结构体内存对齐。

        内存中如何开辟结构体空间?

        首先了解结构体的对其规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
(VS中默认的值为8)

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍
处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数
倍。

        就拿上面的结构体举例,讲解其在内存中的存放情况:

struct S1
{char c1;int c2;char c3;
};

        只有vs有默认对齐数,其他平台是没有默认对齐数的,其他平台对齐数就是数据本身的大小。

        为什么存在内存对齐?

        1、平台原因

        不是多有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定的数据类型,否则抛出硬件异常。

        2、性能原因:

        数据结构(尤其是栈)应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次访问;而对齐的内存访问只需要访问一次。

        总的来说:

        结构体的内存对齐是拿空间换取时间的做法。

在我们大致了解对齐规则后,我们需要了解,结构体定义的时候,位置的不同会影响其空间的大小。让占用空间小的数据尽量放在一起,这样可以减少结构体空间的浪费。

1.7 修改默认对齐数

        在vs中默认对齐数是8,但是它可以被修改。

        使用#pragma

        看例子:

#include <stdio.h>
#pragma pack(4)
struct S
{int i;double d;
};
#pragma pack()int main()
{printf("%d\n", sizeof(struct S));return 0;
}

        用#pragma pack(4)将默认对齐数改成了4,结果从16变成了12。

        后面的#pragma pack()是用来恢复默认对齐数。

1.8 结构体传参

        直接看例子:

#include <stdio.h>
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* p)
{for(int i=0;i<10;i++)printf("%d\n", p->data[i]);
}
int main()
{print1(s);print2(&s);return 0;
}

        结构体传参有两种方式,第一种print1是直接将值传送过去(传值调用);第二种print2是传地址(传址调用)。我们认为第二种更好,因为直接传值的话,参数是需要压栈的,会有时间和空间的系统开销,如果传值一个过大的结构体,会导致系统的性能下降。

总结

        本篇详细讲解了结构体的定义,希望对你有所帮助。

相关文章:

结构体(C语言进阶)(一)

目录 前言 1、结构体声明 1.1 结构体基本概念 1.2 结构体声明 1.3 特殊的结构体声明 1.3.1 匿名结构体声明 1.4 结构体自引用 1.5 结构体变量的定义和初始化 1.6 结构体内存对齐 1.7 修改默认对齐数 1.8 结构体传参 总结 前言 C语言除了有其内置类型&#xff0c;还有…...

【react】对React Router的理解?常用的Router 组件有哪些

1 react-router 是什么 react-router等前端路由的原理大致相同&#xff0c;可以实现无刷新的条件下切换显示不同的页面 路由的本质就是页面的URL发生改变时&#xff0c;页面的显示结果可以根据URL的变化而变化&#xff0c;但是页面不会刷新 因此&#xff0c;可以通过前端路由可…...

生成式 AI

生成式 AI 进入应用爆发期&#xff0c;将极大地推动数字化内容生产与创造。 摘要 生成式 AI &#xff08; Generative AI 或 AIGC &#xff09; 是利用现有文本、音频文件或图像创建 新内容的技术。过去一年&#xff0c;其技术上的 进展主要来自于三大领域&#xff1a;…...

云计算 3月6号 (crontab-计划任务 日志轮转 免密登录)

一、计划任务 计划任务概念解析 在Linux操作系统中&#xff0c;除了用户即时执行的命令操作以外&#xff0c;还可以配置在指定的时间、指定的日期执行预先计划好的系统管理任务&#xff08;如定期备份、定期采集监测数据&#xff09;。RHEL6系统中默认已安装了at、crontab软件…...

Windows Shell命令详解:入门指南

Windows操作系统的Shell命令是执行各种任务和管理系统的关键工具。本文将深入探讨Windows Shell命令的基础知识&#xff0c;介绍常用的命令以及它们的功能和用法&#xff0c;并探讨一些高级技巧&#xff0c;帮助用户更好地利用Shell命令提高工作效率。 1. 什么是Windows Shell命…...

MogDB/openGauss关于PL/SQL匿名块调用测试

MogDB/openGauss 关于 PL/SQL 匿名块调用测试 一、原理介绍 PL/SQL(Procedure Language/Structure Query Language)是标准 SQL 语言添加了过程化功能的一门程序设计语言。 单一的 SQL 语句只能进行数据操作&#xff0c;没有流程控制&#xff0c;无法开发复杂的应用。PL/SQL …...

STP---生成树协议

STP的作用 a)Stp通过阻塞端口来消除环路&#xff0c;并能够实现链路备份目的 b)消除了广播风暴 c)物理链路冗余&#xff0c;网络变成了层次化结构的网络 STP操作 选举一个根桥每个非根交换机选举一个根端口每个网段选举一个指定端口阻塞非根&#xff0c;非指定端口 STP--生成树…...

算法D38| 动态规划1 | 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

理论基础 无论大家之前对动态规划学到什么程度&#xff0c;一定要先看 我讲的 动态规划理论基础。 如果没做过动态规划的题目&#xff0c;看我讲的理论基础&#xff0c;会有感觉 是不是简单题想复杂了&#xff1f; 其实并没有&#xff0c;我讲的理论基础内容&#xff0c;在动…...

Vue教学13:组件的生命周期:掌握组件的每一个关键时刻

大家好&#xff0c;欢迎回到我们的Vue教学系列博客&#xff01;在前十二篇博客中&#xff0c;我们学习了Vue.js的基础知识、安装Node.js与npm、使用Vue Devtools进行调试、Vue实例与生命周期钩子、数据绑定&#xff08;单向与双向&#xff09;、计算属性与侦听器、条件渲染和列…...

mitmproxy代理

文章目录 mitmproxy1. 网络代理2. 安装3. Https请求3.1 启动mitmproxy3.2 获取证书3.3 配置代理3.4 运行测试 4. 请求4.1 读取请求4.2 修改请求4.3 拦截请求 5. 响应5.1 读取响应5.2 修改响应 6. 案例&#xff1a;共享账号6.1 登录bilibili获取cookies6.2 在代理请求中设置cook…...

【GPU驱动开发】- mesa编译与链接过程详细分析

前言 不必害怕未知&#xff0c;无需恐惧犯错&#xff0c;做一个Creator&#xff01; 一、总体框架图 暂时无法在飞书文档外展示此内容 二、Mesa API 处理 OpenGL 函数调用 Mesa API 负责实现 OpenGL 和其他图形 API 的函数接口。Mesa API 表是一个重要的数据结构&#xf…...

如何恢复已删除的华为手机图片?5 种方式分享

不幸的现实是&#xff0c;华为的珍贵时刻有时会因为意外删除、软件故障或其他不可预见的情况而在眨眼之间消失。在这种情况下&#xff0c;寻求恢复已删除的图片成为个人迫切关心的问题。 本文旨在为用户提供如何从华为恢复已删除图片的实用解决方案。我们将探索五种可行的方法…...

通过 python 和 wget 批量下载文件(在Linux/Ubuntu/Debian中测试)

首先创建一个文本文件d.txt, 一行一个链接。 你可以使用简单的 Python 脚本逐行读取文件 (d.txt) 中的链接&#xff0c;并使用 wget 下载文件&#xff1a; import subprocess# File containing download links (replace with your file path) file_path d.txt# Function to …...

个人博客系列-后端项目-RBAC角色管理(6)

设计用户表 ## 用户表 class User(models.Model):username models.CharField(max_length255, uniqueTrue, verbose_name"手机号")password models.CharField(max_length255, uniqueFalse, verbose_name"密码")is_vip models.BooleanField(defaultFalse…...

机器学习-启航

文章目录 原理分析机器学习的两种典型任务机器学习分类总结数据机器学习分类解读简单复杂 原理分析 马克思主义哲学-规律篇 规律客观存在&#xff0c;万事万物皆有规律。 机器学习则是多维角度拆解分析复杂事实数据&#xff0c;发现复杂事实背后的规律&#xff0c;然后将规律用…...

驱动调试第014期-变频调速的原理及相关计算公式应用

一、引言 变频调速是一种通过改变电源频率来实现电动机调速的技术。它具有高效、精确、可靠等优点&#xff0c;广泛应用于工业、商业和家用领域。本文将介绍变频调速的基本原理、优点以及应用领域&#xff0c;并通过详细的公式计算过程和图片说明来帮助读者更好地理解。 二、变…...

JavaWeb环境配置 IDE2022版

一、新建一个javaweb文件 文件名可以自己随意改 二、给建立的项目添加框架支持 勾选Web Application,点击确定 建立成功界面&#xff0c;会生成一个新的web文件夹 三、配置tomcat 1、两种打开配置文件方式&#xff1a; 第一种 第二种 2、打开后&#xff0c;点击号&#xf…...

Matlab偏微分方程拟合 | 完整源码 | 视频教程

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《复杂函数拟合案例分享》本专栏旨在提供 1.以案例的形式讲解各类复杂函数拟合的程序实现方法&#xff0c;并提供所有案例完整源码&#xff1b;2.…...

什么是yocto基本组件(bitbake,recipes,classes,configuration,layer)

文章目录 1基本组件1.1 bitbake1.2 Recipes1.3 Classes1.4 Configurations2 层的理解2.1 层结构2.2 nxp yocto示例2.3 ti yocto示例1基本组件 1.1 bitbake bitbake,是OpenEmbedded构建系统的核心工具,负责解析元数据,从中生成任务列表,然后执行这些任务。bitbake是一个通…...

electron 程序与安装包图标放大与制作

原因 electron-builder 在打包时需要最小支持到256x256像素的icon图标。原有历史图标都太小了。需要尝试将图标放大。 工具 convertio.co/zh/ico-png/ 在线ico转png网站 https://github.com/upscayl/upscayl 图片放大工具 csdn下载 greenfish-icon-editor-pro.en.softonic.c…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

学习 Hooks【Plan - June - Week 2】

一、React API React 提供了丰富的核心 API&#xff0c;用于创建组件、管理状态、处理副作用、优化性能等。本文档总结 React 常用的 API 方法和组件。 1. React 核心 API React.createElement(type, props, …children) 用于创建 React 元素&#xff0c;JSX 会被编译成该函数…...

uni-app学习笔记三十--request网络请求传参

request用于发起网络请求。 OBJECT 参数说明 参数名类型必填默认值说明平台差异说明urlString是开发者服务器接口地址dataObject/String/ArrayBuffer否请求的参数App 3.3.7 以下不支持 ArrayBuffer 类型headerObject否设置请求的 header&#xff0c;header 中不能设置 Refere…...

MySQL技术内幕1:内容介绍+MySQL编译使用介绍

文章目录 1.整体内容介绍2.下载编译流程2.1 安装编译工具和依赖库2.2 下载编译 3.配置MySQL3.1 数据库初始化3.2 编辑配置文件3.3 启动停止MySQL3.4 登录并修改密码 1.整体内容介绍 MySQL技术系列文章将从MySQL下载编译&#xff0c;使用到MySQL各组件使用原理源码分析&#xf…...