C++ 结构体详解

目录

结构体声明

结构体自引用

匿名结构体类型

结构体变量的定义与初始化

匿名结构体的定义与初始化

内存对齐

结构体传参


结构体声明

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

struct tag
{
	member - list;//成员
};

例如,用结构来描述一个学生:

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

注意:此时我们所做的事是在申明一个结构体类型!此时的struct student为一种类型,就如同我们常见的int,char,double...

结构体自引用

在结构中包含一个类型为该结构本身的成员是否可以呢?

我们将结构中包含一个类型为该结构本身的成员的行为叫作结构的自引用。

那么下面这种写法,是否正确呢?

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

答案是这种写法是错误的!

有疑惑的同学不妨假设一下这种写法是正确的,那么我们该如何计算该结构体类型的大小

呢?

当我们计算struct Node的大小时,我们发现结构体里面又有一个struct Node类型的大小需要

我们计算......这样我们就一直循环下去了也算不出结构体的大小。

当我们编译时,就会出来这样的报错:

那正确的做法是怎样的呢?

既然结构体类型是不确定的,那我们就试着用一个该结构体类型的指针,在使用时对这个指针解引用就好了。不管是什么类型的指针,它的大小总是固定的4字节或者8字节。在32位平台上是4字节,在64位平台上是8字节。

顺着这个思路我们就可以这样做:

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

这种样式在学习数据结构的链表时,我们经常会看到它的身影。 

有时候当我们觉得结构体类型名字太长,试图简化的时候,我们可以用typedef来对结构体类型重定义。例如:

·typedef struct student
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
}stu;              //将struct student类型重定义为stu

 那我们就想着,在结构体自引用时,能否偷这个懒呢?

比如:

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

答案是,这种写法是错误的!

当我们语句执行到最后一个分号时,这个typedef的动作才会结束。也就是最后一个分号走完我们才将struct Node类型重定义为Node。所以在此之前编译器是不认识Node这个类型的!

正确的做法:

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

匿名结构体类型

struct 
{
	int a;
};

那么问题来了,匿名结构体省略了结构体标记,下面这种情况第一个结构体与第二个结构体是否类型相同呢?

struct //匿名结构体1
{
	int a;
};
struct //匿名结构体2
{
	int a;
};
int main()
{
 
    return 0;
}

答案是编译器会把上面的两个声明当成完全不同的两个类型。

当然匿名结构体看着花里胡哨,其实作用不大。这个下一段介绍。

结构体变量的定义与初始化

有了结构体类型,那我们就可以来定义一个结构体变量了。

例如现在使用struct student类型来定义几个学生变量:

struct student
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
}s1,s2;           //声明类型的同时定义结构体变量
 
//定义结构体变量
struct student s3;
struct student s4;
int main()
{
	return 0;
}

以上s1,s2,s3,s4就是普通的变量,放在全局中就是全局变量,刚在函数内部就是局部变量。

再来看结构体的初始化:

struct student
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
};
int main()
{
	//初始化:定义变量的同时赋值
	struct student s1 = { "zhangsan",15,"male","12345" };
	return 0;
}

或者这样:

struct student
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
}s1 = { "zhangsan",15,"male","12345" };
int main()
{
	return 0;
}

匿名结构体的定义与初始化

那我们刚才提到的匿名结构体类型该如何定义变量呢?

这样做是否可行呢?

struct
{
	int data;
};
 
struct n1;
struct n2 = { 20 };

答案是,这种写法是错误

编译器直接告诉我们不认识这个东西。

正确的做法其实是这样:

struct
{
	int data;
}n1,n2={20};

我们必须在声明匿名结构体类型时就定义变量。否则出了这个村就没这个店了。所以它就相当于一次性产品,不过,匿名结构体看似鸡肋,但有时候也会很有用。而且,匿名结构体类型更加做不到自引用了!

内存对齐

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

以我们目前的印象,S1的大小这样算:1+4+1=6

那我们运行一下看结果对不对呢?

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

那我们究竟该如何计算结构体的大小呢?

首先得掌握结构体的 对齐规则 :
1. 第一个成员在与结构体变量偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
                这里有两个点需要说明:
                偏移量: 偏移量就是程序的 逻辑地址 与段首的差值。
                对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
        VS中默认的值为8
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的
整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

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

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

切记:最后一步一定要检查结构体大小是否为最大对齐数的整数倍!

了解了内存对齐的规则之后我们思考一下为什么要存在内存对齐

大部分的参考资料都是如是说的:

1. 平台原因 ( 移植原因 ) :
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取
某些特定类型的数据,否则抛出硬件异常。
例如:某硬件平台规定,在一块地址上访问int类型的数据时,只找偏移量为4的整数倍的地址。
这时,如果没有内存对齐,int类型的数据有可能存储在偏移量非4的整数倍的位置上。那就会
出现访问不到的错误。

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

总体来说:

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

那在设计结构体的时候,我们既要满足对齐,又要节省空间,所以我们这样做:

让占用空间小的成员尽量集中在一起。

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

S1和S2虽然存储的内容类型相同,但是二者所占空间大小却不相同。

修改默认对齐数

认识了默认对齐数,有时候默认对齐数的大小使用起来可能会造成不方便,这时候我们就可以修改

默认对齐数。如下:

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

结构体传参

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

如上所示两种结构体传参的形式其实是有优劣之分的。

函数在创建形参时,参数需要压栈,会在空间和时间上有开销。

当结构体所包含的内容过大时,一定程度上会导致程序的性能下降,所以我们选择第二种方法。

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

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

相关文章

自定义构建jdk镜像

&#xff08;1&#xff09;准备jdk压缩包、创建Dockerfile文件 jdk压缩包、Dockerfile文件在同一目录&#xff0c;如下 Dockerfile文件内容如下 # 指定基础镜像 FROM centos:latest # 作者和电子邮件 MAINTAINER vinegar93 "vinegar93163.com" # 指定工作目录 WORK…

el-date-picker时间控制范围为过去时间不可选

<el-date-picker :picker-options"startPickerOptions()" value-format"yyyy-MM-dd HH:mm:ss" v-model"form.applyFixPlan" type"datetime" placeholder"选择日期时间"> </el-date-picker> 在method中定义star…

数据库管理-第123期 Oracle相关两个参数(202301205)

数据库管理-第123期 Oracle相关两个参数&#xff08;202301205&#xff09; 最近在群聊中看到俩和Oracle数据库相关的俩参数&#xff0c;一个是Oracle数据库本身的&#xff0c;一个是来自于Weblogic的&#xff0c;挺有趣的&#xff0c;本期研究一下。&#xff08;本期涉及参数…

记录一次浏览器报错Whitelabel Error Page

后端打包prod的时候错误打包成了dev,部署到服务器上导致访问静态资源的时候全部报这个错.

【CentOS】配置 Apache 服务

yum install httpd -y# 查看是否安装成功 httpd -v # 出现版本号表示成功# 启动服务 systemctl start httpd# 查看状态 systemctl status httpd # running 即可成功 ● httpd.service - The Apache HTTP ServerLoaded: loaded (/usr/lib/systemd/system/httpd.service; disable…

人才招聘信息网的设计与实现

摘 要 随着经济的高速发展&#xff0c;人才的流动也越来越频繁&#xff0c;怎样才能用最少的精力和时间来招聘人才的企业要求相一致&#xff0c;也让应聘人参加应聘是企业和个人都关心的问题。 本网站采用基于广域网的B/S结构平台&#xff0c;比C/S有更强的适用范围&#xff0…

漏洞复现--万户ezoffice wpsservlet任意文件上传

免责声明&#xff1a; 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…

Linux基本指令(2.0)

周边知识&#xff1a; 1.Linux中&#xff0c; 一切皆文件 构建大文件 输入如下shell命令 i1; while [ $i -le 10000]; do echo "hello Linux $i"; let i; done 此时大文件已经创建在big.txt 此时我们发现cat查看无法查看开始内容 我们使用more 当占满一屏之后就不…

[adbd] adb添加密码登录SHELL

项目中设备为Linux&#xff0c;使用ADB进行调试&#xff0c;为了安全需要在adb shell时进行密码认证。 在此记录一下&#xff0c;防止遗忘~~~ 修改 system/core/adb/services.c 文件的登录shell为 /bin/login 这样就可以使用linux自带的认证服务 如果想强制指定某个用户进行登…

快手数仓面试题附答案

题目 1 讲一下你门公司的大数据项目架构&#xff1f;2 你在工作中都负责哪一部分3 spark提交一个程序的整体执行流程4 spark常用算子列几个&#xff0c;6到8个吧5 transformation跟action算子的区别6 map和flatmap算子的区别7 自定义udf&#xff0c;udtf&#xff0c;udaf讲一下…

嵌入式硬件和软件哪个好?

嵌入式硬件和软件哪个好? 嵌入式软硬件工程师哪个更有前途呢?一起来看看。 嵌入式是分为软硬件工程师的&#xff0c;首先我们先来看看嵌入式硬件工程师吧! 嵌入式硬件开发工程师主要编写嵌入式系统硬件总体方案和详细方案&#xff0c;要求理解嵌入式系统架构&#xff0c;有一…

unity | 动画模块之循环滚动选项框

一、作者的话 评论区有人问&#xff0c;有没有竖排循环轮播选项框&#xff0c;我就写了一个 二、效果动画 如果不是你们想要的&#xff0c;就省的你们继续往下看了 三、制作思路 把移动分成里面的方块&#xff0c;还有背景&#xff08;父物体&#xff09;&#xff0c;方块自…

SI24R03 高度集成低功耗SOC 2.4G 收发一体芯片

今天给大家介绍一款Soc 2.4G 收发一体模块-SI24R03 Si24R03是一款高度集成的低功耗无线SOC芯片&#xff0c;芯片为QFN32 5x5mm封装&#xff0c;集成了资源丰富的MCU内核与2.4G收发器模块&#xff0c;最低功耗可达1.6uA&#xff0c;极少外围器件&#xff0c;大幅降低系统应用成本…

电子学会C/C++编程等级考试2023年03月(四级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:最佳路径 如下所示的由正整数数字构成的三角形: 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最…

spring 框架的 AOP

AOP依赖导入 <!-- AOP依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>

Git版本控制---入门学习

1.简介 是一个免费的开源分布式版本控制系统工具&#xff0c;旨在快速高效地处理从小型到大型的所有项目。 它是由 Linus Torvalds 在2005年创建的&#xff0c;用于开发 Linux 内核。 Git具有大多数团队和开发人员所需的功能、性能、安全性和灵活性。 它还用作重要的分布式版本…

【matlab程序】matlab画子图的多种样式

【matlab程序】matlab画子图的多种样式

openEuler操作系统安装

所需要的软件镜像 https://repo.openeuler.org/openEuler-20.03-LTS/ISO/x86_64/ 选择openEuler-20.03-LTS-everything-x86_64-dvd.iso 版本的最完整 如果硬盘空间小可选择openEuler-20.03-LTS-x86_64-dvd.iso 安装步骤 1 选择第一个 install openEuler 20.03-LTS 2 选择语…

ultralytics yolo图像分类训练案例;pytorch自有数据集图像分类案例

1、ultralytics yolo图像分类训练案例 优点:使用方便,训练过程评估指标可以方便查看 缺点:自带模型少,可选择自定义小 参考:https://docs.ultralytics.com/tasks/classify/#val https://blog.csdn.net/weixin_42357472/article/details/131412851 1)数据集格式 https://…

quickapp_快应用_快应用与h5交互

快应用与h5交互 h5跳转到快应用[1] 判断当前环境是否支持组件跳转快应用[2] h5跳转到快应用(1)deeplink方式进行跳转(推荐)(2)h5点击组件(接收参数存在问题)(3)url配置跳转(官方不推荐) 问题-浏览器问题 web组件h5页面嵌入快应用快应用发送消息到h5页面h5页面接收快应用发送的消…