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

news2025/3/10 10:52:29

目录

前言

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是传地址(传址调用)。我们认为第二种更好,因为直接传值的话,参数是需要压栈的,会有时间和空间的系统开销,如果传值一个过大的结构体,会导致系统的性能下降。

总结

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

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

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

相关文章

MySQL基础-----SQL语句之DCL数据控制语句

目录 前言 一、管理用户 1.查询用户 2.创建用户 3.修改用户密码 4.删除用户 案例 二、权限控制 1.查询权限 2.授予权限 3.撤销权限 案例 前言 本期我们学习SQL语句的最后一部分内容&#xff0c;也就是数据控制语句DCL。DCL英文全称是Data Control Language(数据控制语…

Rust错误处理和Result枚举类异常错误传递

Rust 有一套独特的处理异常情况的机制&#xff0c;它并不像其它语言中的 try 机制那样简单。 首先&#xff0c;程序中一般会出现两种错误&#xff1a;可恢复错误和不可恢复错误。 可恢复错误的典型案例是文件访问错误&#xff0c;如果访问一个文件失败&#xff0c;有可能是因…

初学C++

注释 变量 作用&#xff1a;给一段指定的内存空间起名&#xff0c;方便操作这段内容 数据类型 变量名 变量初始值; 常量 用于记录程序中不可更改的数据 宏常量&#xff1a; #define 宏常量 常量值 const修饰的变量&#xff1a; const 数据类型 常量名 常量值; 关键字 …

C#,回文分割问题(Palindrome Partitioning Problem)算法与源代码

1 回文串 “回文串”是一个正读和反读都一样的字符串&#xff0c;初始化标志flagtrue&#xff0c;比如“level”或者“noon”等等就是回文串。 2 回文分割问题 给定一个字符串&#xff0c;如果该字符串的每个子字符串都是回文的&#xff0c;那么该字符串的分区就是回文分区。…

【EI会议征稿通知】2024年计算机视觉、机器人与自动化工程国际学术会议(CRAE 2024)

2024年计算机视觉、机器人与自动化工程国际学术会议&#xff08;CRAE 2024&#xff09; 2024 International Conference on Computer Vision, Robotics and Automation Engineering&#xff08;CRAE 2024&#xff09; 2024年计算机视觉、机器人与自动化工程国际学术会议&…

华为配置智能升级功能升级设备示例

配置智能升级功能升级设备示例 组网图形 图1 配置智能升级功能组网图 背景信息组网需求配置思路前提条件操作步骤操作结果 背景信息 为了方便用户及时了解设备主流运行版本&#xff0c;快速完成升级修复&#xff0c;华为设备支持自动下载、自助升级功能。用户在设备Web网管…

精细调度:Apache DolphinScheduler脚本深度解析

在现代数据处理和工作流管理中&#xff0c;Apache DolphinScheduler以其灵活性和强大的调度能力受到开发者的广泛欢迎。 本文将逐步解析DolphinScheduler的关键脚本&#xff0c;希望能提供一个详尽的操作指南&#xff0c;帮助大家掌握安装、配置和操作的每一步。 建立在./bin/e…

基于机器学习的垃圾分类

1绪论 1.1问题背景 垃圾分类有减少环境污染、节省土地资源、再生资源的利用、提高民众价值观念等的好处&#xff0c;在倡导绿色生活&#xff0c;注重环境保护的今天&#xff0c;正确的垃圾分类和处理对我们的生态环境显得尤为重要。 在国外很多国家&#xff0c;经过了几十年…

解决WordPress更新插件或者更新版本报WordPress 需要访问您网页服务器的权限的问题

文章目录 前言一、原因二、解决步骤总结 前言 当对WordPress的插件或者版本进行更新时报错&#xff1a;要执行请求的操作&#xff0c;WordPress 需要访问您网页服务器的权限。 请输入您的 FTP 登录凭据以继续。 如果您忘记了您的登录凭据&#xff08;如用户名、密码&#xff09…

JAVA 用二分法查找数组中是否存在某个值

二分法查找的概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法。首先&#xff0c;将表中间位置记录的关键字与查找关键字比较&#xff0c;如果两者相等&#xff0c;则查找成功&#xff1b;否则利用中间位置记录将表分成…

每日一题——LeetCode1588.所有奇数长度子数组的和

方法一 暴力循环 遍历数组的每一个元素&#xff0c;找寻该元素所有连续长度为奇数的可能&#xff0c;累加奇数长度区间内的所有元素。 比如对于[1,4,2,5,3] &#xff0c;对于第一个元素1&#xff0c;有[1]、[1,4,2]、[1,4,2,5,3]这三种可能&#xff0c;对于第二个元素4&#x…

STP---生成树协议

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

Java 数据结构之链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA null || headB null) return null;ListNode pA headA, pB headB;while (pA ! pB) {pA pA null ? headB : pA.next;pB pB null ? headA : pB.next;}return pA;} public ListNode rev…

Linux cgrpup技术解析和验证测试

Linux cgrpup技术解析和验证测试 1. cgroup技术解析和分类1.1. 相关概念介绍1.2 cgroup子系统 2. cgroup子系统详解2.1 cpu子系统2.2 cpuacct子系统2.3 cpuset子系统2.4 memory子系统2.5 blkio子系统2.6 ns子系统 3. cgroup使用3.1 通用使用流程3.1.1 限制进程的cpu资源3.1.2 绑…

【开源项目】ollama:本地部署大模型

文章目录 1. 安装2. 使用体验2.1. 运行llama22.2. 运行llama2-chinese 项目地址&#xff1a;Github - ollama/ollama 注意&#xff1a;项目对硬盘容量、内存要求比较高。 1. 安装 从Github项目的最新release下载安装包&#xff0c;点击运行即可。 2. 使用体验 2.1. 运行ll…

中小企业如何降低网络攻击和数据泄露的风险?

德迅云安全收集了Bleeping Computer 网站消息&#xff0c; Arctic Wolf 表示 Akira 勒索软件组织的攻击目标瞄准了中小型企业&#xff0c;自 2023 年 3 月以来&#xff0c;该团伙成功入侵了多家组织&#xff0c;索要的赎金从 20 万美元到 400 多万美元不等&#xff0c;如果受害…

基于OpenCV的图形分析辨认05(补充)

目录 一、前言 二、实验内容 三、实验过程 一、前言 编程语言&#xff1a;Python&#xff0c;编程软件&#xff1a;vscode或pycharm&#xff0c;必备的第三方库&#xff1a;OpenCV&#xff0c;numpy&#xff0c;matplotlib&#xff0c;os等等。 关于OpenCV&#xff0c;num…

常见排序算法解析

芝兰生于深林&#xff0c;不以无人而不芳&#xff1b;君子修道立德&#xff0c;不为穷困而改节 文章目录 插入排序直接插入排序希尔排序 选择排序直接选择排序堆排序 交换排序冒泡排序快速排序优化挖坑法前后指针法非递归版 归并排序递归非递归 总结 插入排序 插入排序&#…

论文阅读笔记 | MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment

文章目录 文章题目发表年限期刊/会议名称论文简要动机主要思想或方法架构实验结果 文章链接&#xff1a;https://doi.org/10.48550/arXiv.2004.05508 文章题目 MetaIQA: Deep Meta-learning for No-Reference Image Quality Assessment 发表年限 2020 期刊/会议名称 Publi…

Unity性能优化篇(八) 导入的模型网格优化设置

模型导入Unity后&#xff0c;可以选中这个模型&#xff0c;在Inspector窗口设置它的属性。下面说的都是可自定义选择优化的地方 Model选择卡: 1.在Model选项卡&#xff0c;启用Mesh Compression可以压缩模型&#xff0c;压缩程度越高&#xff0c;模型精度越低&#xff0c;但是…