【C语言】struct结构体

news2024/11/28 4:31:43

文章目录

  • 一. 结构体简述
  • 二. 结构体的声明和定义
    • 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;  //对齐数1
    struct s //对齐数4
    {
            int c; //对齐数4
            char d;//对齐数1
    }g;//此处定义并申明了结构体变量,在这里需要把结构体
       //看成一个整体,独立计算这个结构体的大小为8字节
       //结构体整体的对齐数是内部最大成员的对齐数
       //之后把这个结构体看出对齐数为4,大小为8的成员
    char f; //对齐数1
    int 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 中有明确的规定允许结构体中最后一个数组大小是未知的。

柔性数组其实是结构体中的最后一个数组未说明大小,且结构体中至少包含一个以上其他类型的成员,如:

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++ 有。

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

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

相关文章

用 AudioGPT 输入自然语言,可以让 ChatGPT 唱歌了?

夕小瑶科技说 原创作者 | 智商掉了一地 借助 ChatGPT 强大的理解与生成能力&#xff0c;结合基础语音模型&#xff0c;集成模型 AudioGPT 诞生了&#xff01; 最近基于 ChatGPT 的二创如雨后春笋一样冒出&#xff0c;上周我们一起看了黑客松优秀作品大赏&#xff0c;这周又有新…

HCIP-7.2VLAN间通信单臂、多臂、三层交换方式学习

VLAN间通信单臂、多臂、三层交换方式学习 1、单臂路由2、多臂路由3、三层交换机的SVI接口实现VLAN间通讯3.1、VLANIF虚拟接口3.2、VLAN间路由3.2.1、单台三层路由VLAN间通信&#xff0c;在一台三层交换机内部VLAN之间直连。3.2.2、两台三层交换机的之间的VLAN通信。3.2.3、将物…

Ae:画笔面板

Ae菜单&#xff1a;窗口/画笔 Brushes 快捷键&#xff1a;Ctrl 9 画笔 Brushes面板提供了画笔预设及画笔设置&#xff08;包括画笔动态&#xff09;等的参数设置。 画笔预设 Brush Presets 画笔预设窗格中提供了自带的画笔预设&#xff0c;主要有尖角 Hard Round、柔角 Soft R…

【云原生】Epinio--Kubernetes 的应用程序开发引擎

Kubernetes 已成为容器编排的事实标准&#xff0c;改变了我们的开发流程。十年前&#xff0c;我们只需要将代码打包成 war/jar 包&#xff0c;然后启动应用即可。然而&#xff0c;现在面向 Kubernetes 的开发&#xff0c;交付的产物有可能是 Helm Chart、Workload Yaml、Docker…

SOFA Weekly|SOFAArk 社区会议预告、Layotto 社区会议回顾、社区本周贡献

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…

【SPSS】相关分析和偏相关分析详细操作过程(附案例实战)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

Vben Admin 自学记录 —— 介绍及使用(持续更新中...)

Vue-Vben-Admin 一个基于 Vue3.0、Vite、Ant-Design-Vue、TypeScript的后台解决方案&#xff0c;为开发中大型项目提供开箱即用的解决方案。 包含功能&#xff1a; 二次封装组件utilshooks动态菜单权限校验按钮级别权限控制… 安装 1.拉取代码 从GitHub上 git clone https:/…

「OceanBase 4.1 体验」|大厂开始接入的国产分布式数据库,不来了解了解?

OceanBase 4.1 体验 前言OCP Express在线升级功能租户级物理备库TP&#xff08;事务处理&#xff09;和AP&#xff08;分析处理&#xff09;优化TP 性能优化AP 性能优化 结尾 前言 上次我们讲了本人自己亲自上手OceanBase 4.1的初体验&#xff0c;国产的分布式数据库也太太太太…

HCIA-RS实验-路由配置-简述RIPv1 和RIPv2

简述 RIPv1和RIPv2是两种不同版本的路由信息协议&#xff08;Routing Information Protocol&#xff09;&#xff0c;用于在网络中传递路由信息。它们的主要区别在于以下几点&#xff1a; 1. 分类&#xff1a;RIPv1是一种类别路由协议&#xff08;classful routing protocol&a…

Python实现图像的手绘效果

用Python实现手绘图像的效果 1.图像的RGB色彩模式 图像一般使用RGB色彩模式&#xff0c;即每个像素点的颜色由红、绿(G)、蓝(B)组成。RGB三个颜色通道的变化和叠加得到各种颜色&#xff0c;其中&#xff1a; R红色&#xff0c;取值范围&#xff0c;0-255G绿色&#xff0c;取值…

将时间序列转换为分类问题

本文将以股票交易作为示例。我们用 AI 模型预测股票第二天是涨还是跌。在此背景下&#xff0c;比较了分类算法 XGBoost、随机森林和逻辑分类器。文章的另外一个重点是数据准备。我们必须如何转换数据以便模型可以处理它。 在本文中&#xff0c;我们将遵循 CRISP-DM 流程模型&a…

机器学习强基计划8-4:流形学习等度量映射Isomap算法(附Python实现)

目录 0 写在前面1 什么是流形&#xff1f;2 什么是流形学习&#xff1f;3 等度量映射原理4 Python实现 0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器学习…

【经验分享】Windows/Ubuntu上如何使用api下载kaggle上的数据集

1 下载kaggle的api 1.1 已经安装了Anaconda 打开cmd&#xff08;Windows&#xff09;/打开终端&#xff08;Ubuntu&#xff09; conda activate 你的conda环境名称 这里我的环境叫做Pytorch conda activate Pytorch然后执行pip install kaggle pip install kaggle如果你没…

沃尔玛、亚马逊影响listing的转化率4大因素,测评补单自养号解析

1、listing的相关性&#xff1a;前期我们在找词&#xff0c;收集词的时候&#xff0c;我们通过插件来协助我们去筛选词。我们把流量高&#xff0c;中&#xff0c;低的关键词都一一收集&#xff0c;然后我们再进行对收集得来的关键词进行分析&#xff0c;再进行挑词&#xff0c;…

云计算:优势与未来趋势

文章目录 前言一、云计算的优势1. 降低IT成本2. 提高工作效率3. 提高业务的可靠性和稳定性4. 提升安全性 二、未来发展趋势1. AI与云计算的融合2. 边缘计算的发展3. 多云的趋势4. 服务器和存储的创新 三、 行业应用案例1.金融行业2.医疗保健行业3.教育行业4.零售和物流行业 四、…

2.4 定点除法运算

学习目标&#xff1a; 学习如何实现一个基于余数查商法的定点除法运算&#xff0c;并能够正确地进行除法计算&#xff0c;包括处理舍入误差和溢出等问题。具体要求包括&#xff1a; 熟悉定点数的表示方法和定点数的基本运算法则&#xff0c;理解定点除法运算的基本概念和原理。…

逻辑回归评分系统(mimic数据集)

1.读取数据与数据处理 为什么不对数据进行标准化&#xff1f; 我们制作的评分卡&#xff0c;评分卡是要给医务人员们使用的基于病人的化验结果打分的一张卡片&#xff0c;而为了制作这张卡片&#xff0c;我们需要对我们的数据进行一个“分档”&#xff0c;比如说&#xff0c;…

大型Saas系统的权限体系设计(一)

X0 概述 在2B系统开发中&#xff0c;权限体系设计是绕不开的问题。最简单的当然是RBAC模型&#xff0c;只要通过用户、角色、权限几个有限的概念&#xff0c;就可以建立起一套基本可用的权限体系。再复杂一点&#xff0c;可以增加角色的层级概念&#xff0c;使得角色的配置更高…

验证回文串

题目&#xff1a;验证回文串 思路&#xff1a; 这段代码是一个判断字符串是否为回文的函数。它接受一个 string 类型的参数 s&#xff0c;并依次执行两个步骤&#xff1a; 首先对字符串进行预处理&#xff1a; 将大写字母转换成小写字母&#xff1b;移除非字母数字字符。 然…

平均10870元!2023一季度居民可支配收入公布(文末附最新招聘岗位)

今天是五一假期的第一天&#xff0c;暂别职场的打工人已经开始扎入人从众中放肆玩乐了&#xff0c;小编已经流下了羡慕的泪水。不过&#xff0c;今年的五一除了人流量上暴涨之外&#xff0c;出行成本也没被少吐槽&#xff0c;机票咱就不说了&#xff0c;酒店民宿的涨幅简直到了…