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

【C语言】struct结构体

文章目录

  • 一. 结构体简述
  • 二. 结构体的声明和定义
    • 1、简单地声明一个结构体和定义结构体变量
    • 2、声明结构体的同时也定义结构体变量
    • 3、匿名结构体
    • 4、配合typedef,声明结构体的同时为结构体取别名
    • 5、在声明匿名结构体时,使用typedef给这个匿名结构体取别名
  • 三. 结构体变量的初始化
  • 四. 结构体成员的访问方法
  • 五. 结构体大小的计算
    • 1. 计算方法
    • 2. 普通结构体
    • 3. 包含数组成员的结构体
    • 4. 成员包含结构体的结构体
    • 5. 成员包含联合体的结构体
    • 6. 空结构体的大小
  • 六. 柔性数组
    • 1. 介绍
    • 2. 使用方法
    • 3. 柔性数组的特点
  • 七. C++ 中 struct 与 class 的区别
  • 八. C 和 C++ 结构体的区别

一. 结构体简述

具有相同或不同类型元素的集合叫做结构体。定义一个结构体,本质是在制作一个类型:

// 声明一个学生信息结构体
struct Student
{char name[20];int age;
};int main()
{// 定义出两个学生变量struct Student s1 = { "张三", 18};struct Student s2 = { "李四", 20};return 0;
}

二. 结构体的声明和定义

1、简单地声明一个结构体和定义结构体变量

在C中,结构体内只能存放各种类型的变量,不能存函数:
在这里插入图片描述

像上面这样就是声明了一个结构体struct Student,此时的 struct Student 相当于一个类型名。

然后我们可以用这个自己声明的结构体类型去定义变量:
在这里插入图片描述

补充:C 和 C++ 中定义结构体变量的区别

  • 在 C 中使用结构体去定义变量时,需要在结构体名称前加上 struct 关键字。
  • 在 C++ 中使用结构体去定义变量时,可以不加 struct 关键字

在这里插入图片描述

2、声明结构体的同时也定义结构体变量

在这里插入图片描述
也许初期看不习惯容易困惑,其实这就相当于两步合并一步:先定义结构体 struct Student,再定义变量 s1 和 s2:

3、匿名结构体

使用方式:声明结构体的时候缺失结构体名,同时定义出一个或n个结构体变量:
在这里插入图片描述

这种形式只能使用在声明结构体的同时也定义出结构体变量,由于没有结构体名,因此后续不可以再定义新的结构体变量。

4、配合typedef,声明结构体的同时为结构体取别名

在这里插入图片描述

前面说过,使用结构体去定义结构体变量时,C 需要加 struct,C++ 不需要。那么使用结构体的别名去定义变量呢?

答:使用结构体别名去定义结构体变量时,C 和 C++ 都不需要加 struct,加了反而都会报错,因为取别名时把struct连同结构体名称一起包含进去了。

5、在声明匿名结构体时,使用typedef给这个匿名结构体取别名

在这里插入图片描述

这种形式声明了一个缺失结构体名的结构体,但同时使用 typedef 为结构体设置了别名,所以之后我们可以使用这个别名,去定义结构体变量。

三. 结构体变量的初始化

先弄清楚变量初始化和赋值的区别:

struct Student
{char name[20];int age;
};int main()
{// 变量刚开始创建时给值,这个叫初始化struct Student s1 = {"nick", 18};// 变量创建后,再对它的值进行操作这个叫赋值strcpy(s1.name, "tony");s1.age = 24;return 0;
}

结构体只能被整体初始化,不能被整体赋值,想要赋值的话只能把成员逐个地取出来再赋值。
在这里插入图片描述

补充:数组也是一样的道理:只能整体初始化,不能整体赋值。如果是字符数组想要整体赋值的话,可以使用 strcpy 函数:
在这里插入图片描述

本人推测结构体和数组不能被整体赋值的原因是:它们内部空间在逻辑上是独立一块块的,所以我们只能对这些独立的空间逐个赋值,而不能整体赋值。

四. 结构体成员的访问方法

我们可以通过变量或变量的地址去访问结构体的成员。

struct Student
{char name[20];int age;
};int main()
{// 1、通过变量访问结构体成员struct Student s;strcpy(s.name, "张三");s.age = 18;// 2、通过指针访问结构体成员struct Student* p = &s;printf("%s\n", p->name);printf("%d\n", p->age);return 0;
}--------结果如下--------
张三
18

为什么结构体会有两种访问方式?

在函数传参(传值、传址)时,会生成临时变量,如果要传的结构体变量太大的话,传值拷贝出来的临时对象也会很大,如果用传地址的方式来传结构体变量地址的话,可以很好的节省空间。

在这里插入图片描述
当然如果可以直接拿到结构体变量的话,使用变量来访问结构体成员会更直观点。

五. 结构体大小的计算

1. 计算方法

结构体的大小不是结构体元素单纯相加就行的,因为我们现在主流的计算机使用的都是64位字长的CPU,对这类型的CPU取8个字节的数要比取一个字节要高效,也更方便。所以在结构体中每个成员的首地址都是8的整数倍的话,取数据元素时就会相对更高效,这就是内存对齐的由来。每个特定平台上的编译器都有自己的默认“对齐系数”(也叫对齐模数)。程序员可以通过预编译命令#pragma pack(n)来改变这一系数,其中的 n 就是你要指定的“对齐系数”。

但实际每个成员的类型可能是不同的,每个类型对应不同大小,为了更高效地读取结构体变量的成员,结构体的大小要遵循一套对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处。(即结构体的首地址处,即对齐到0处)
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
  3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

对齐数 = 该结构体成员变量自身的大小与编译器默认的一个对齐数的较小值。

PS:VS中的默认对齐数为8,不是所有编译器都有默认对齐数,当编译器没有默认对齐数的时候,成员变量的大小就是该成员的对齐数。

2. 普通结构体

第一步:找出每个成员变量的大小将其与编译器的默认对齐数相比较,取其较小值为该成员变量的对齐数
在这里插入图片描述
PS:这里使用的是VS编译器,故默认对齐数为8。

第二步:根据每个成员对应的对齐数画出它们在内存中的相对位置
在这里插入图片描述
第三步:通过最大对齐数决定最终该结构体的大小

通过图我们可以知道,绿色部分(double d成员占用)+红色部分(char c成员占用)+紫色部分(int i成员占用)+红色与紫色之间的白色部分(浪费掉了)总共占用了16个字节的内存空间。

我们需要将它们总共占用的内存空间(16)与结构体成员的最大对齐数(8)相比较,结构体的总大小为最大对齐数的整数倍,此时16正好是8的整数倍,所以该结构体在VS编译器下的大小就16个字节。即创建一个该类型的结构体变量,内存需为其开辟16个字节的内存空间。

PS:大多数情况下,成员变量已经占用的总字节个数并不一定正好为其成员变量中的最大对齐数的整数倍,这时我们需要将其扩大为最大对齐数的整数倍。

3. 包含数组成员的结构体

数组应拆开来看,不能看做一个整体

struct S
{char a; //对齐数为1。占1个字节char c[5]; //对齐数为1。可看成5个char占5个字节int b; //对齐数为4。占4个字节,因为前面所有成员占6个字节,不是4//个字节的整数倍,所以在第二个成员和第三个成员//之间要补2个字节
} //所以该结构体的大小为1+5+2(补)+4=12个字节

4. 成员包含结构体的结构体

1)如果结构体成员只是说明而没有定义变量,则这个结构体成员不占内存空间。

struct S
{char a; //对齐数为1。占1个字节struct s{int c;char d;}; //此处结构体只声明,没有定义结构体变量,所以该声明//的结构体在地址空间中并不占位置int f; //对齐数为4。占4个字节double b; //对齐数为8,
}; //该结构体的大小为1+3(补)+4+8=16个字节

2)如果内部定义并申明了其他结构体变量,这时需要把这个结构体看成一个整体,大小要独立计算,至于对齐数取其内部最大成员的对齐数。

struct t
{char a;  //对齐数1struct s //对齐数4{int c; //对齐数4char d;//对齐数1}g;//此处定义并申明了结构体变量,在这里需要把结构体//看成一个整体,独立计算这个结构体的大小为8字节//结构体整体的对齐数是内部最大成员的对齐数//之后把这个结构体看出对齐数为4,大小为8的成员char f; //对齐数1int b;  //对齐数4
}; //所以该结构体的大小为1+3(补)+8+1+3(补)+4=20个字节

5. 成员包含联合体的结构体

联合体的大小等同于联合体里面最大成员的大小,所以可以把联合体等效成一个变量,这个变量就是联合体里面最大的那个成员。

和前文所说的结构体一样,如果只声明联合体,没定义联合体变量,则联合体就当成不存在。

struct t
{char a;union s{int c;char d;double h;}g; int f;double b;
};//所以该结构体的大小为1+7(补)+8+4+4(补)+8=32个字节

6. 空结构体的大小

1)在 VS2017 下测试
在这里插入图片描述

2)在 Centos7 下测试
在这里插入图片描述

六. 柔性数组

1. 介绍

在 c99 中有明确的规定允许结构体中最后一个数组大小是未知的。

  • 数组作为结构体的最后一个成员
  • 数组元素可以不写或写成0
  • 结构体中至少包含一个以上处数组外的其他类型的成员
struct T
{int a;char b;int arr[];//或者int arr[0];};int main()
{struct T t;// sizeof 求结构体大小时所求出的大小没有包括柔性数组的大小printf("%lu\n", sizeof(struct T));return 0;
}--------结果如下--------
8

2. 使用方法

包含柔性数组的结构体,可以把整个结构体看成是变长的。

#include<stdio.h>   
#include<stdlib.h>                                          
#include<stdlib.h>     struct d
{                                                                                    int nb;  int nn;                                 int arr[];                 
};                         int main()                      
{//分别给结构体中其他类型的成员和柔性数组申请空间struct d *p=(struct d*)malloc(sizeof(struct d)+5*sizeof(int));p->nb=100;p->nn=50;  for(int i=0;i<5;i++){p->arr[i]=i;//赋值printf("%d ",p->arr[i]);}     //重新调整所申请的空间,将柔性数组调整为40。struct d *pp=(struct d*)realloc(p,48); if(pp!=NULL){p=pp;for(int i=5;i<10;i++){p->arr[i]=i;//赋值printf("%d ",p->arr[i]);    } free(p);p=NULL;}                                                                                                    return 0;                                                                           
}       --------结果如下--------
0 1 2 3 4 5 6 7 8 9                                                                                                                                                                                                       

3. 柔性数组的特点

  1. 柔性数组只需在 malloc 创建时要独立于结构体申请空间,此后的 realloc 再分配空间和 free 释放都只需对一个结构体指针操作即可。
  2. 柔性数组申请的内存更加集中,有利于查找使用和减少内存碎片。
  3. sizeof 求结构体大小时所求出的大小没有包括柔性数组的大小。

七. C++ 中 struct 与 class 的区别

  • class 成员的默认权限为 private,struct 成员的默认权限为 public。
  • class 的继承默认是 private 继承,struct 的继承默认是 public 继承。
  • class 可以作为一个关键字定义模板参数(与 typename 作用一样),而struct 不可以。

八. C 和 C++ 结构体的区别

  1. C++ 结构体内部可以有成员变量和成员函数,而 C 中结构体只能有成员变量。
  2. C 结构体的成员变量不能在声明时给初值,而 C++ 中可以
    在这里插入图片描述
  3. C++ 中定义结构变量时,可以不在名称前面加上 struct 关键字,而 C 一定要
  4. C 结构体内不能有静态成员,而 C++ 可以。
  5. C 结构没有访问修饰限定符,而 C++ 有。

相关文章:

【C语言】struct结构体

文章目录 一. 结构体简述二. 结构体的声明和定义1、简单地声明一个结构体和定义结构体变量2、声明结构体的同时也定义结构体变量3、匿名结构体4、配合typedef&#xff0c;声明结构体的同时为结构体取别名5、在声明匿名结构体时&#xff0c;使用typedef给这个匿名结构体取别名 三…...

Docker代码环境打包

1. 介绍 Docker是一种开源的容器化平台&#xff0c;它可以在操作系统级别运行应用程序。通过将应用程序及其依赖项封装成一个可移植的容器&#xff0c;Docker使得应用程序可以在任何环境中轻松部署、运行和管理。使用Docker&#xff0c;开发人员可以避免在不同环境中出现的配置…...

现代CMake高级教程 - 第 6 章:输出与变量

双笙子佯谬老师的【公开课】现代CMake高级教程课程笔记 第 6 章&#xff1a;输出与变量 在运行 cmake -B build 时&#xff0c;打印字符串&#xff08;用于调试&#xff09; message("Hello world!")❯ cmake --build buildHello world! -- Configuring done -- G…...

windows/linux文件传输

windows系统下文件传输-FTP python安装pyftpdlib模块 pip install pyftpdlib 这里可能会出现报错&#xff0c;自己看着更换源解决 然后运行python&#xff0c;在2121端口监听 python -m pyftpdlib 然后我们可以使用windows命令行进行操作&#xff0c;自己可以去看下相关文…...

Anoconda安装笔记+win10 更改中文用户名为英文

win10 更改中文用户名为英文 ① WinR打开命令窗口&#xff0c;输入regedit 打开注册表&#xff0c; 手动找到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\ProfileList 在这个目录下面有几个S-1-5-的项&#xff0c;挨个检查每一项&#xff0c; 找到“…...

Java Web应用开发 ——作业七

一.单项选择题&#xff08;共7题,28.7分&#xff09; 1 Servlet程序的入口点是&#xff08; &#xff09;。 A、 init&#xff08;&#xff09; B、 main&#xff08;&#xff09; C、 service&#xff08;&#xff09; D、 doGet&#xff08;&#xff09; 正确答案&#…...

echo,date,bc命令详解

文章目录 echo&#xff0c;date&#xff0c;bc命令详解echo(输出文本)date(显示日期的命令)date命令的--date选项date命令 bc(高精度计算器) echo&#xff0c;date&#xff0c;bc命令详解 echo(输出文本) echo命令是一个常用的Shell命令&#xff0c;用于在终端上输出文本。它…...

【Java笔试强训 29】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;求正数数…...

如何在微服务下保证事务的一致性

随着业务的快速发展、业务复杂度越来越高&#xff0c;传统单体应用逐渐暴露出了一些问题&#xff0c;例如开发效率低、可维护性差、架构扩展性差、部署不灵活、健壮性差等等。而微服务架构是将单个服务拆分成一系列小服务&#xff0c;且这些小服务都拥有独立的进程&#xff0c;…...

华为OD机试 - 新学校选址(Python)

题目描述 为了解新学期学生暴涨的问题,小乐村要建立所新学校, 考虑到学生上学安全问题,需要所有学生家到学校的距离最短。 假设学校和所有学生家都走在一条直线之上,请问学校建立在什么位置, 能使得到学校到各个学生家的距离和最短。 输入描述 第一行: 整数 n 取值范围 [1…...

thinkphp6结合layui增删改查综合案列

文章目录 技术栈实现代码实现数据库 本案例适合新手&#xff0c;特别是杠刚入门thinkphp和layui&#xff0c;但又不是特别熟悉这类 主要实现登录退出功能&#xff0c;用户模块的增删改查功能&#xff0c;分页功能是layui表单自带功能 效果图 左侧的菜单栏我没有写对应的页面&am…...

PostgreSQL数据库以任意时间间隔聚合查询group by

文章目录 业务场景以固定时间&#xff08;年/月/日/时/分/秒&#xff09;聚合to_char聚合date_trunc聚合 以任意时间聚合date_bin聚合实际应用 业务场景 我们做的是交通信控平台&#xff0c;需要根据实时采集到的交通大数据&#xff0c;计算出一些指标&#xff0c;存储到数据库…...

sql注入(二)盲注,二次注入,宽字节注入

目录 目录 一、布尔盲注 1.判断库名的长度 2.判断数据库名 2.1判断数据库名首字符 2.2 判断数据库名的其余字符 二、时间盲注&#xff1a; 1.判断库名的长度 2.判断库名&#xff1a; 3.判断表名payload&#xff1a; 4.爆出列名 5.爆数据 三、二次注入 1.原理&#…...

Linux 基础操作

Linux学习教程&#xff0c;Linux入门教程&#xff08;超详细&#xff09; chown (change owner) &#xff1a; 修改所属用户与组。chmod (change mode) &#xff1a; 修改用户的权限。 要查看文件或目录的属性&#xff0c;可以使用ls命令&#xff0c;加上-l选项。例如&#xff…...

2.4 等比数列

学习步骤&#xff1a; 如果我要学习等比数列&#xff0c;我会按照以下步骤进行学习&#xff1a; 定义和性质&#xff1a;首先了解等比数列的定义和性质&#xff0c;包括公比、首项、通项公式、求和公式等。 例题练习&#xff1a;通过练习一些简单的例题来理解等比数列的概念和…...

2022年新能源汽车专题讲座

2022年新能源汽车专题讲座 单选题&#xff08;共5题&#xff0c;每题6分&#xff09; 1、《中华人民共和国数据安全法》自&#xff08;&#xff09;起施行。 正确答案&#xff1a;C、2021年9月1日 2、典型的智能汽车结构主要分为&#xff08;&#xff09;个层次。 正确答案…...

Git操作远程仓库

远程仓库 码云 https://gitee.com/ 是国内的一个代码托管平台&#xff0c;由于服务器在国内&#xff0c;所以相比于GitHub&#xff0c;码云速度会更快 码云使用流程 注册账号----登录码云-----点击新建仓库----记得保存地址 GitHub https://github.com/ 是一个面向开源…...

制造策略 ETO、MTO、ATO、MTS

ETO 按交货周期跨度从长到短来讲&#xff0c;首先就是 ETO&#xff0c;Engineer To Order – 面向订单设计、定制生产或特殊生产。 就是客户给的订单&#xff0c;你要生产的话&#xff0c;你之前的原产品改动很大&#xff0c;或者基本上用不上&#xff0c;要完全按照客户的要求…...

Git(六):基本命令(3):储藏、标签、拉取、子模块

目录 17、stash 储藏 17.2 描述 17.3 基本用法 18、tag 标签 18.1 描述 18.2 基本用法 19、fetch 获取 19.1 描述 19.2 基本用法 20、pull 整合 20.1 描述 20.2 基本用法 20.3 pull 与 fetch 的区别 21、push 更新推送 21.1 描述 21.2 基本用法 22、remote 管…...

7.0、Java继承与多态 - 多态的特性

7.0、Java继承与多态 - 多态的特性 面向对象的三大特征&#xff1a;封装性、继承性、多态性&#xff1b; extends继承 或者 implements实现&#xff0c;是多态性的前提&#xff1b; 用学生类创建一个对象 - 小明&#xff0c;他是一个 学生&#xff08;学生形态&#xff09;&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

linux arm系统烧录

1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 &#xff08;忘了有没有这步了 估计有&#xff09; 刷机程序 和 镜像 就不提供了。要刷的时…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

全面解析数据库:从基础概念到前沿应用​

在数字化时代&#xff0c;数据已成为企业和社会发展的核心资产&#xff0c;而数据库作为存储、管理和处理数据的关键工具&#xff0c;在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理&#xff0c;到社交网络的用户数据存储&#xff0c;再到金融行业的交易记录处理&a…...