C++ struct 笔记(超级详细)
今日碎碎念:我在学C语言时经常用到结构体struct,之后在写C++程序时遇到在struct中定义构造函数和成员函数的情况,这在c语言中是从未遇到过的,觉得奇怪,想到之前并没有真正系统学习C++里的struct,有必要今天详细记录一下。
虽然今天结构体较少使用,但知道它们是什么,以及如何使用它们仍然很重要,这并不仅仅是因为可以在较老的程序中遇到它们,还因为在某些情况(这里请看二、C++ class和struct的区别)下,类的实例无法使用,这时必须使用结构体。
目录
一、C++ struct的用法
1、struct是什么
2、 struct的4种声明定义方式
3、结构体的2种初始化方式
4、结构体成员的访问
5、结构体的嵌套
6、将结构体作为参数和返回值
二、C++ class和struct的区别
三、C++ struct和 c struct的区别
四、struct计算结构体某个成员相对于结构体基址的偏移
一、C++ struct的用法
1、struct是什么
struct是程序员定义的数据类型,将逻辑上连接在一起的不同类型的数据组合到一起的单元。
2、 struct的4种声明定义方式
a. 第一种语法表示
struct 结构体名称
{
数据类型 member1;
数据类型 member2;
};
#include<iostream>
#include <string>
using namespace std;
struct SStudent
{int nNo;std::string strName;
};int main(int argc, char *argv[])
{//使用初始化列表初始化structstruct SStudent s1 = { 1, "ying"}; //C++定义struct变量时,前面的struct可以写SStudent s2 = { 2, "Ming"}; //C++定义struct变量时,前面的struct也可以不写cout << s1.nNo << endl;cout << s1.strName << endl;cout << s2.nNo << endl;cout << s2.strName << endl;return 0;
}
b.第二种语法表示
typedef struct 结构体名称{
数据类型 member1;
数据类型 member2;
}结构体名称别名;
这种情况使用typedef关键字,声明了数据类型的别名,所以在定义结构体 变量时有两种方式:
第一种:结构体名称 构体变量名
第二种:结构体名称别名 结构体变量名
#include<iostream>
#include <string>
using namespace std;typedef struct SStudent
{int nNo;std::string strName;
} SStud;int main(int argc, char *argv[])
{SStudent s1 = { 1, "ying"}; //使用结构体名称SStud s2 = { 2, "Ming"}; //使用结构体名称别名cout << s1.nNo << endl;cout << s1.strName << endl;cout << s2.nNo << endl;cout << s2.strName << endl;return 0;
}
c.第三种语法表示
struct 结构体名称{
数据类型 member1;
数据类型 member2;
}结构体变量;
#include<iostream>
#include <string>
using namespace std;
struct SStudent
{int nNo;std::string strName;
} stu;int main(int argc, char *argv[])
{
// stu s2; //错误,没有使用typedef关键字,stu是变量不是类型stu = { 1, "ying"}; //使用初始化表赋值stu.nNo = 2; //修改成员nNo值stu.strName = "XiaoMing";//修改成员strName值cout << stu.nNo << endl;cout << stu.strName << endl;return 0;
}
d.第四种语法表示
//匿名结构体
struct {
数据类型 member1;
数据类型 member2;
}结构体变量名;
在声明的同时定义了结构体变量,但不能在其它地方声明,因为我们无法得知该结构体的标识符,所以就无法通过标识符来声明变量。
#include<iostream>
#include <string>
using namespace std;//声明了结构体并定义(且只能在这里定义)了两个该结构体的变量,
//由于无法获取结构体名称(因为匿名),所以无法在其他地方定义该结构体变量
struct
{int nNo;std::string strName;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 1, "ying"}; //使用初始化表赋值stu2 = { 2, "Ming"}; //使用初始化表赋值cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu2.nNo << endl;cout << stu2.strName << endl;return 0;
}
3、结构体的2种初始化方式
a. 使用初始化列表
语法:
//初始化列表中的项目是按照结构体声明中成员的顺序依次赋值,各个成员的赋值用逗号分隔并用大括号括起来
结构体类型 变量名 = {member1取值,member2取值,member3取值...};
前面的例子都使用了初始化列表来初始化,这里不再记录例子。
几个注意点:
1)使用初始化列表时可以仅仅初始化部分成员,如果某个成员未被初始化,则所有跟在它后面的成员都需要保留为未初始化。C++未提供跳过某个成员初始化其他成员的方法。
#include<iostream>
#include <string>
using namespace std;//声明了结构体,增加nAge成员,并且定义了两个变量stu1和stu2
struct SStudent
{int nNo;std::string strName;int nAge;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 1, "ying"}; //合法,仅初始化了nNo和strName,不初始化nAge
// stu2 = { 2, 11}; //非法,不能跳过strName给nAge赋值cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu1.nAge << endl;cout << stu2.nNo << endl;cout << stu2.strName << endl;cout << stu2.nAge << endl;return 0;
}
2)可以在结构体声明中使用默认值初始化结构体成员。
#include<iostream>
#include <string>
using namespace std;//声明了结构体,并且定义了两个变量stu1和stu2
struct SStudent
{int nNo = 1;std::string strName = "YunCai";int nAge = 6;
} stu1, stu2;int main(int argc, char *argv[])
{ stu1 = { 2, "ying"}; //不给nAge赋值,将取值默认值6cout << stu1.nNo << endl;cout << stu1.strName << endl;cout << stu1.nAge << endl;cout << stu2.nNo << endl; //不给stu2赋值,所有成员都使用默认值cout << stu2.strName << endl;cout << stu2.nAge << endl;return 0;
}
运行结果:
b. 使用构造函数
与类构造函数一样,结构体的构造函数必须是与结构体名称相同的公共成员函数,并且没有返回类型。因为默认情况下,所有结构体成员都是公开的,所以不需要使用关键字 public。
#include<iostream>
#include <string>
using namespace std;//声明了结构体,
//并且定义了一个带有三个默认参数的构造函数
//在定义SStudent 变量而不向其传递任何参数时,提供默认值
struct SStudent
{int m_nNo;string m_strName;int m_nAge;SStudent(int nNo = 1, string strName = "YunCai", int nAge = 6){m_nNo = nNo;m_strName = strName;m_nAge = nAge;}
};int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数SStudent stu2(2, "ying"); //调用构造函数并传参nNo = 2, strName = "ying"cout << stu1.m_nNo << endl;cout << stu1.m_strName << endl;cout << stu1.m_nAge << endl;cout << stu2.m_nNo << endl;cout << stu2.m_strName << endl;cout << stu2.m_nAge << endl;return 0;
}
4、结构体成员的访问
结构体数据成员都是public的,所以它们可以被直接访问,并且可以像常规变量一样使用。
结构体变量使用点运算符.访问数据成员,结构体指针使用->指针运算符访问数据成员。
#include<iostream>
#include <string>
using namespace std;struct SStudent
{int m_nNo;string m_strName;int m_nAge;SStudent(int nNo = 1, string strName = "YunCai", int nAge = 6){m_nNo = nNo;m_strName = strName;m_nAge = nAge;}
};int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数SStudent stu2(2, "ying"); //调用构造函数并传参nNo = 2, strName = "ying"SStudent* stu3 = new SStudent(3, "Ming", 8); //使用new初始化SStudent对象指针cout << stu1.m_nNo << endl;//使用点运算符访问cout << stu1.m_strName << endl;cout << stu1.m_nAge << endl;cout << stu2.m_nNo << endl;cout << stu2.m_strName << endl;cout << stu2.m_nAge << endl;cout << stu3->m_nNo << endl;//使用指针运算符访问cout << stu3->m_strName << endl;cout << stu3->m_nAge << endl;delete stu3;stu3 = nullptr;return 0;
}
5、结构体的嵌套
就像一个类的对象可以作为成员放在另一个类中,一个结构体的变量可以作为成员放在另一个结构体中。
#include<iostream>
#include <string>
using namespace std;//声明了结构体SDate日期
struct SDate
{int m_nYear;int m_nMonth;int m_nDay;SDate(const int& nY = 2000, const int& nM = 11, const int& nD = 2){m_nYear = nY;m_nMonth = nM;m_nDay = nD;}
};//声明了结构体SStudent学生,其中m_dateBirth是学生出生日期
struct SStudent
{int m_nNo;string m_strName;int m_nAge;SDate m_dateBirth;SStudent(const int& nNo = 1, const string& strName = "YunCai",const int& nAge = 6, const SDate& dateBirth= SDate()){m_nNo = nNo;m_strName = strName;m_nAge = nAge;m_dateBirth = dateBirth;}
};//使用一个函数打印学生信息,参数类型为常量引用SStudent
void print(const SStudent& stu)
{cout << stu.m_nNo << " " << stu.m_strName << " " << stu.m_nAge << " ";cout <<"Birthday:" << stu.m_dateBirth.m_nYear << "_" <<stu.m_dateBirth.m_nMonth << "_" << stu.m_dateBirth.m_nDay << endl;
}//修改学号增加100,参数类型为值传递,返回值为SStudent
SStudent UpdateNo1(SStudent stu)
{stu.m_nNo += 100;return stu;
}//修改学号增加100,参数类型为引用传递
void UpdateNo2(SStudent& stu)
{stu.m_nNo += 100;
}int main(int argc, char *argv[])
{ SStudent stu1; //调用构造函数并且使用默认参数print(stu1);stu1 = UpdateNo1(stu1);print(stu1);SDate stu2_birthday(1999, 9, 10);SStudent stu2(2, "Ying", 7, stu2_birthday);//调用够用构造函数并且传了4个参数print(stu2);UpdateNo2(stu2);print(stu2);return 0;
}
运行结果:
6、将结构体作为参数和返回值
a.将结构体作为参数:与类对象一样,结构体变量也可以通过值、引用和常量引用传递给函数。
1) 值传递:需要生成整个原始结构的副本并传递给函数。因为不希望浪费时间来复制整个结构体,所以,除非结构很小,否则一般会通过引用将结构体传递给函数。
2)引用传递:引用传递不会生成原始结构体的副本,函数可以直接访问原始结构体的成员变量,也可能更改它们。
3)常量引用传递:常量引用传递也不会生成原始结构体的副本,函数直接访问原始结构体的成员变量但不能修改原始结构体的成员变量。
b.将结构体作为返回值:函数的返回类型是结构体的名称。
在5、结构体的嵌套中的例子,print函数使用了常量引用传递进行传值,UpdateNo1函数的参数类型为值传递,返回值为结构体,UpdateNo2函数的参数类型为引用传递。
二、C++ class和struct的区别
结构体和类基本雷同,唯一区别是,类中成员变量默认为私有private,而结构体中则为公有public。因此在使用时,我们可以根据不同的场景或者需求来选择使用struct或者class。
a.这些情况下使用struct比class更好
1)纯数据结构:如果一个类只包含数据成员,而没有成员函数;
2)数据成员全部为public;
3)用于C接口:如果一个类需要与C语言交互,例如作为C语言库的接口,那么使用struct更加合适(因为C语言不支持类的概念,而使用struct可以更加方便地进行数据传递);
4)继承自C结构体:如果一个类需要继承自一个C语言的结构体,那么使用struct更加合适。因为C结构体默认为public,并且在C++中可以使用struct来继承。
b.这些情况下使用class比struct更好
1)需要保证数据的安全性,对数据的访问控制比较严格:使用class可以将数据成员设置为私有成员private,防止外部直接修改数据;
2)需要进行多态:如果一个类需要进行多态操作,例如需要使用虚函数,那么使用class更加合适。
三、C++ struct和 c struct的区别
a. C++中定义结构体变量时可以省略struct,但C语言中不可以省略;
b. C++中struct可以和类一样,有访问权限,并可以定义成员函数; C语言中struct没有访问权限的设置,是一些变量的集合体,不能定义成员函数;
c.C++中struct可以继承,也可以实现多态;C语言中struct不支持继承和多态;
四、struct计算结构体某个成员相对于结构体基址的偏移
如何计算:
#define offsetof(s,m) (size_t)&(((s *)0)->m)
解释:
((s *)0):强制转化成数据结构指针,并使其指向地址0;
((s *)0)->m:使该指针指向成员m
&(((s *)0)->m):获取该成员m的地址
(size_t)&(((s *)0)->m):转化这个地址为合适的类型
使用场景:
offsetof已经被定义在系统库中,所以直接使用offsetof计算结构体某个成员相对于结构体基址的偏移
例子:
要计算如下SDate中m_nDay成员相对于结构体基址的偏移,可以使用 offsetof(SDate, m_nDay)。
struct SDate{
int m_nYear;
int m_nMonth;
int m_nDay;
};
参考资料如下:
1、C++ struct的4种定义方式_c++ struct 定义_Mr顺的博客-CSDN博客
2、C++结构体完全攻略(超详细)
3、什么时候以struct 替代class? - 知乎
相关文章:

C++ struct 笔记(超级详细)
今日碎碎念:我在学C语言时经常用到结构体struct,之后在写C程序时遇到在struct中定义构造函数和成员函数的情况,这在c语言中是从未遇到过的,觉得奇怪,想到之前并没有真正系统学习C里的struct,有必要今天详细…...

Vue基础1:生命周期汇总(vue2)
Description 生命周期图: 可以理解vue生命周期就是指vue实例从创建到销毁的过程,在vue中分为9个阶段:创建前/后,载入前/后,更新前/后,销毁前/后,其他;常用的有:created&…...

Linux串口驱动
《I.MX6ULL 参考手册》第 3561 页的“Chapter 55 Universal Asynchronous Receiver/Transmitter(UART) I.MX6ULL串口原理 1.1UART与USART UART是异步通信,USART是异步/同步通信,比UART多了一条时钟线 USART 的全称是 Universal Synchronous/Asynchr…...

java反编译工具jd-gui使用
文章目录 一、JD-GUI介绍二、下载三、安装四、使用教程五、免责声明摘抄 一、JD-GUI介绍 JD-GUI是一个独立的图形实用程序,显示“.class”文件的Java源代码。 使用JD-GUI浏览重构的源代码,以便即时访问方法和字段。 二、下载 MAC安装包:ht…...

Linux 之 shell 脚本
Linux 之 shell 脚本 1、脚本的格式要求2、shell 脚本常用执行方式2.1、绝对路径或相对路径方式2.2、sh脚本方式(不推荐) 3、shell 的变量3.1、定义 shell 变量3.2、将命令返回值赋值给变量 (重点)3.3、设置环境变量3.4、位置参数…...

如何去阅读开源的第三方库的源码
2023年9月2日,周六晚上 今天探索了一天如何去阅读第三方库的源码,终有所获。 再结合以前看cereal项目的源码的经验,于是就有了这篇博客。 我个人认为: 在阅读一个开源的第三方库之前,要先学会这个第三方库的一些简单…...

浅析Linux虚拟网络技术
文章目录 概述Tap/tun设备tun/tap的工作机制 Bridge网桥Bridge的工作机制Bridge IP 相关参考 概述 在传统的网络环境中,一台物理主机包含一张或多张网卡,要实现与其它物理主机之间的通信,需要将自身的网卡通过路由器或者交换机连接到外部的物…...

设计模式之九:迭代器与组合模式
有许多方法可以把对象堆起来成为一个集合(Collection),比如放入数组、堆栈或散列表中。若用户直接从这些数据结构中取出对象,则需要知道具体是存在什么数据结构中(如栈就用peek,数组[])。迭代器…...

官方推荐:6种Pandas读取Excel的方法
Pandas提供了多种读取Excel文件的方法,以下是官方推荐的6种方法: 1. 使用pd.read_excel()函数 这是最常用的方法,可以读取Excel文件,并将其转换为Pandas数据框。可以指定工作表名和列名的行号。 df pd.read_excel(data.xlsx, …...

Redis与Mysql区别
一、关系型数据库 mysql,pgsql,oracle ,sqlserver 支持连表关联查询(会有一些特定的语法特特性) 二、非关系型数据库 redis,mongodb,memcache (key-value) 三、关系型数据库与非关系型数据库的区别: 1&am…...

Black-Box Tuning for Language-Model-as-a-Service
本文是LLM系列的文章,针对《Black-Box Tuning for Language-Model-as-a-Service》的翻译。 语义模型即服务的黑盒调整 摘要1 引言2 背景3 方法4 实验5 讨论与未来工作 摘要 GPT-3等超大的预训练语言模型(PTM)通常作为服务发布。它允许用户设…...

通用的ARM64架构镜像
#此链接包含x86架构和ARM架构的pytorch镜像,镜像里面已下载好各种第三方库,GPU版本的pytorch可用。缺点:镜像有点大 测试环境:操作系统麒麟银河V10,ARM64处理器(cpu),显卡为T4显卡 …...

git大文件推送报错
报错信息 不多掰扯,直接上报错信息和截图 Delta compression using up to 8 threadsRPC failde; HTTP 413 curl 22 The requested URL returned error: 413 Request Entity Too Large从以上的报错信息不难看出推送仓库的时候,请求体过大,为…...

RDMA性能优化经验浅谈
一、RDMA概述 首先我们介绍一下RDMA的一些核心概念,当然了,我并不打算写他的API以及调用方式,我们更多关注这些基础概念背后的硬件执行方式和原理,对于这些原理的理解是能够写出高性能RDMA程序的关键。 Memory Region RDMA的网…...

day 44 | ● 309.最佳买卖股票时机含冷冻期 ● 714.买卖股票的最佳时机含手续费
309.最佳买卖股票时机含冷冻期 此外,在返回的时候,由于状态234都是卖出的状态,所以要比较其最大值进行返回。 func maxProfit(prices []int) int {dp : make([][]int, len(prices))dp[0] make([]int, 4)dp[0][0] -prices[0]for i : 1; i &…...

电子科大软件系统架构设计——系统分析与设计概述(含课堂作业、练习答案)
系统分析与设计概述 信息系统概述 what 信息系统是一种能够完成对业务数据进行采集、转换、加工、计算、分析、传输、维护等信息处理,并能就某个方面问题给用户提供信息服务的计算机应用系统。 组成 信息化基础设施(计算机、计算机网络、服务器、系统…...

【SpringMVC】@RequestMapping注解(详解)
文章目录 前言1、RequestMapping注解的功能2、RequestMapping注解的位置3、RequestMapping注解的value属性4、RequestMapping注解的method属性1、对于处理指定请求方式的控制器方法,SpringMVC中提供了RequestMapping的派生注解2、常用的请求方式有get,po…...

8.(Python数模)马尔科夫链预测
Python实现马尔科夫链预测 马尔科夫链原理 马尔科夫链是一种进行预测的方法,常用于系统未来时刻情况只和现在有关,而与过去无关。 用下面这个例子来讲述马尔科夫链。 如何预测下一时刻计算机发生故障的概率? 当前状态只存在0(故…...

什么是浏览器缓存(browser caching)?如何使用HTTP头来控制缓存?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 浏览器缓存和HTTP头控制缓存⭐ HTTP头控制缓存1. Cache-Control2. Expires3. Last-Modified 和 If-Modified-Since4. ETag 和 If-None-Match ⭐ 缓存策略⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 记得点击…...

谁需要了解学习RPA?什么地方可以使用RPA?
RPA(Robotic Process Automation)是一种通过软件机器人自动化执行特定任务和流程的技术。以下是一些需要了解RPA的人群: 企业决策者:企业决策者需要了解RPA的潜在收益和风险,以及如何将其纳入企业的数字化转型战略中。…...

Qt各个版本下载及安装教程(离线和非离线安装)
Qt各个版本下载链接: Index of /archive/qthttps://download.qt.io/archive/qt/ 离线安装 ,离线安装很无脑,下一步下一步就可以。 我离线下载 半个小时把2G的exe下载下来了...

使用爬虫代码获得深度学习目标检测或者语义分割中的图片。
问题描述:目标检测或者图像分割需要大量的数据,如果手动从网上找的话会比较慢,这时候,我们可以从网上爬虫下来,然后自己筛选即可。 代码如下(不要忘记安装代码依赖的库): # -*- co…...

代码随想录算法训练营第39天 | ● 62.不同路径 ● 63. 不同路径II
文章目录 前言一、62.不同路径二、63.不同路径II总结 前言 动态规划 一、62.不同路径 深搜动态规划数论 深搜: 注意题目中说机器人每次只能向下或者向右移动一步,那么其实机器人走过的路径可以抽象为一棵二叉树,而叶子节点就是终点&#…...

《网站建设:从规划到发布的全过程详解》
一、引言 在数字时代,网站已经成为企业和个人在互联网上的重要存在。一个优质网站的建立需要周全的规划、设计、开发、测试和发布。本文将详细介绍网站建设的全过程,帮助读者了解和掌握网站建设的流程和方法。 二、网站建设的意义 网站建设具有以下意…...

1分钟实现 CLIP + Annoy + Gradio 文搜图+图搜图 系统
多模态图文搜索系统 CLIP 进行 Text 和 Image 的语义EmbeddingAnnoy 向量数据库实现树状结构索引来加速最近邻搜索Gradio 轻量级的机器学习 Web 前端搭建 文搜图 图搜图 CLIP图像语义提取功能!...

用树形dp+状压维护树上操作的计数问题:0902T3
发现操作数 k ≤ 6 k\le6 k≤6,可以考虑对操作进行状压。 然后找找性质,发现要么删掉一棵子树,要么进去该子树。可以视为每种操作有两种情况。 然后分讨一下当前该如何转移。 树形dp的顺序: 合并子树考虑当前往上的边的方向 …...

【python爬虫】批量识别pdf中的英文,自动翻译成中文上
不管是上学还是上班,有时不可避免需要看英文文章,特别是在写毕业论文的时候。比较头疼的是把专业性很强的英文pdf文章翻译成中文。我记得我上学的时候,是一段一段复制,或者碰到不认识的单词就百度翻译一下,非常耗费时间。本文提供批量识别pdf中英文的方法,后续文章实现自…...

Android笔记--Hilt
Hilt 是 Android 的依赖项注入库,可减少在项目中执行手动依赖项注入的样板代码。执行手动依赖项注入要求您手动构造每个类及其依赖项,并借助容器重复使用和管理依赖项。依赖注入的英文是Dependency Injection,简称DI,简单说一个类中使用的依赖…...

Oracle常用权限处理
对于Oracle来说,用户等于Schema,创建用户即创建Schema -- 创建用户 create user TCK_TEXT identified by "TCKTCK"; --赋予登陆权限 grant connect to TCK_TEXT; --查看权限列表 select * from user_role_privs ; select * from user_sys_priv…...

Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作
Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 目录 Stable Diffuse 之 本地环境部署 WebUI 进行汉化操作 一、简单介绍 二、汉化操作 附录: 一、Install from URL 中出现 Failed to connect to 127.0.0.1 port 7890: Connection refused 错误…...