C语言—结构体

1.结构体类型的声明

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

1.1结构的简单声明

struct xxx
{member——list;
}xxxxx;

例如:描述一本书

struct BOOK
{char Book_Name[20];char author[20];float price;int id[18];
}BOOK1;// 全局的变量int main()
{struct BOOK BOOK2;//局部的变量......
}

1.2 结构的特殊声明

在声明结构的时候,可以不完全的声明。(匿名结构体类型)

这两个结构体类型都是匿名的,所以编译器会认为这是两个结构体定义,那么对应的地址肯定就是不一样的。

  • 编译器会把上面的两个声明当成完全不同的两个类型,所以是非法的。
  • 匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

1.3 结构的自引用

定义一个链表的结点:

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

上述代码是否正确?

其实是不正确的,因为当你计算这个结构体的大小时,在发现它一直在递归,这样大小就是无限大的,是不合理的。

所以正确的自引用方式:

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

在结构体自引用使用的过程中,夹杂了 typedef 对匿名结构体类型重命名,也容易引入问题,看看下述代码是否可行?

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

不行,因为Node是对前面的结构体类型的重命名产生的,但是在结构体内部提前使用Node类型来创建成员变量是不可以的。

所以要这样:

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

2.结构体内存对齐

不是6~

2.1对齐规则

  1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。VS中默认的值是8,Linux中gcc没有默认对齐数,对齐数就是成员自身的大小。
  3. 结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数的最大的)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数的整数倍。

2.2对齐规则存在的原因

  1. 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成39的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被放在两个8字节内存块中。

总的来说:结构体的内存对齐就是拿空间来换取时间

所以在设计结构体的时候,我们要进行设计,使其满足对齐,也尽量节省空间:让空间小的放在一起。

2.3修改默认对齐数

#pragma 这个预处理指令,可以修改编译器的默认对齐数。

#include <stdio.h>
#pragma pack (1)//设置默认对齐数为1
struct S1
{char c1;char c2;int n;
};struct S2
{char c1;int n;char c2;
};
#pragma pack ()//取消设置的对齐数,还原为默认int main()
{printf("%zd\n", sizeof(struct S1));printf("%zd\n", sizeof(struct S2));return 0;
}

3.结构体传参

#include <stdio.h>struct S
{int arr[1000];int n;char ch;
};void print1(struct S tmp)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", tmp.arr[i]);}printf("\n");printf("n = %d\n", tmp.n);printf("ch = %c\n", tmp.ch);}void print2(struct S* ps)
{int i = 0;for (i = 0; i < 10; i++){printf("%d ", ps->arr[i]);}printf("\n");printf("n = %d\n", ps->n);printf("ch = %c\n", ps->ch);
}int main()
{struct S s = { {1,2,3,4,5,6,7,8,9,10},55,'L' };print1(s);print2(&s);return 0;
}

上面的print1和print2函数哪个好些?

首选print2函数。

原因:函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。

           如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

结论:

结构体传参的时候,要传结构体的地址。

4.结构体实现位段

4.1什么是位段

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是 int、unsigned int 或 signed int ,在C99中位段成员的类型也可以选择其他类型。
  2. 位段的成员名后边有一个冒号和一个数字。

位段中的位指的是二进制位。

一定程度上减少内存占用。

4.2位段的内存分配

  1. 位段的成员可以是int、unsigned int、signed int 或者是char等类型的
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
  3. 位段设计很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

4.3 位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,如果写成27,在16位机器会出现问题)
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余位还是利用,这是不确定的。
#include <stdio.h>struct S
{int a : 3;int b : 4;int c : 5;int d : 4;
};int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;printf("%zd\n", sizeof(struct S));return 0;
}

这是一种方式,举个例子。

总结:跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

作者自述:本文主要针对C语言的结构体的知识。内容中包含了很多总结内容。本文制作不易,求求动动你们发财的小手点个赞和关注,这是对我创造最大的动力。后续我也会跟进内容,尽量一周至少一次,保证内容的质量。如果有想知道的内容或者有建议的地方,欢迎后台私信或者在本文留言哦。感谢各位的支持捏Thanks♪(・ω・)ノ。

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

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

相关文章

面向卫星遥感的红外微小舰船目标检测方法:MTU-Net

论文简介 空间红外微小舰船检测旨在从地球轨道卫星所拍摄的图像中识别并分离出微小舰船。由于图像覆盖面积极其广大&#xff08;如数千平方公里&#xff09;&#xff0c;这些图像中的候选目标相比空中或地面成像设备观测到的目标&#xff0c;尺寸更小、亮度更低且变化更多。现有…

mayavi pyqt 实例

目录 安装&#xff1a; 示例代码&#xff1a; 生成3d检测框&#xff1a; 显示立方体 两个窗口 安装&#xff1a; pip install vtk pip install mayavi pip install PyQt5 pip install pyqt5 mayavi traits traitsui 示例代码&#xff1a; import sys from PyQt5.QtWidg…

如何在Java中实现缓存机制?

如何在Java中实现缓存机制&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将深入探讨在Java应用程序中如何实现高效的缓存机制。缓存是提高…

温湿度采集及OLED显示

目录 软件I2C和硬件I2C每隔2秒钟采集一次温湿度数据&#xff0c;显示到OLED上&#xff0c;同时通过串口发送到上位机的“串口助手”软件 软件I2C和硬件I2C "I2C"代表Inter-Integrated Circuit&#xff0c;是一种用于在数字电路之间进行通信的串行通信协议。软件I2C和…

使用Testcontainers进行Java集成测试

在现代软件开发中&#xff0c;集成测试是确保应用程序与其依赖项&#xff08;如数据库、消息队列等&#xff09;正确交互的关键步骤。Testcontainers是一个支持JUnit测试的Java库&#xff0c;它提供了一个简单而强大的方式来创建和管理测试所需的任何Docker容器。本文将详细介绍…

【PythonWeb开发】Flask请求中传递参数到视图函数的方法总结。

在Flask中&#xff0c;传入参数主要有两种常见的方式&#xff0c;即通过GET请求和POST请求。 一、GET请求传递参数 &#xff08;1&#xff09;URL路径中获取 这种类型的参数通常称为路径参数或路由参数&#xff0c;它们是URL路径的一部分&#xff0c;通过尖括号<parameter…

CV每日论文--2024.6.21

1、An Image is Worth More Than 16x16 Patches: Exploring Transformers on Individual Pixels 中文标题&#xff1a;一张图像的价值超过 16x16 的补丁&#xff1a;在单个像素上探索 Transformers 简介&#xff1a;这项工作并非介绍一种新的方法&#xff0c;而是呈现了一项有…

@ControllerAdvice:你可以没用过,但是不能不了解

1.概述 最近在梳理Spring MVC相关扩展点时发现了ControllerAdvice这个注解&#xff0c;用于定义全局的异常处理、数据绑定、数据预处理等功能。通过使用 ControllerAdvice&#xff0c;可以将一些与控制器相关的通用逻辑提取到单独的类中进行集中管理&#xff0c;从而减少代码重…

WinForm 2048

WinForm 2048 是一个基于 Windows 窗体应用程序&#xff08;WinForms&#xff09;实现的经典益智游戏——2048。这个游戏通过简单的滑动或点击操作&#xff0c;将相同数字的方块合并&#xff0c;以生成更大的数字方块&#xff0c;最终目标是创造出一个数字为 2048 的方块。 游…

电商爬虫API的定制开发:满足个性化需求的解决方案

一、引言 随着电子商务的蓬勃发展&#xff0c;电商数据成为了企业决策的重要依据。然而&#xff0c;电商数据的获取并非易事&#xff0c;特别是对于拥有个性化需求的企业来说&#xff0c;更是面临诸多挑战。为了满足这些个性化需求&#xff0c;电商爬虫API的定制开发成为了解决…

【杂记-浅谈IPv6地址】

IPv6地址 一、IPv6地址概述二、IPv6地址结构三、IPv6地址分类四、IPv6地址配置五、IPv6的应用场景 一、IPv6地址概述 IPv6&#xff0c;Internet Protocol version 6&#xff0c;是互联网协议的第六版&#xff0c;旨在克服IPv4地址耗尽的挑战&#xff0c;并为互联网的未来发展提…

Apache Tomcat 10.1.25 新版本发布 java 应用服务器

Tomcat 是一个小型的轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发和调试 JSP 程序的首选。对于一个初学者来说&#xff0c;可以这样认为&#xff0c;当在一台机器上配置好 Apache 服务器&#xff0c;可利用它响应对 H…

uniapp 使用uview 插件

看创建项目版本vue2 、 vue3 Button 按钮 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 1. npm install uview-ui2.0.36 2. // main.js&#xff0c;注意要在use方法之后执行 import uView from uview-ui Vue.use(uView) // 如此配置即可 uni.$u.c…

服务治理入门

服务治理的生命周期 在微服务架构中&#xff0c;服务治理是确保服务正常运行和高效协作的关键。服务治理的生命周期包括以下五个阶段&#xff1a;服务注册、服务发现、服务续约/心跳、服务被动剔除和服务主动剔除。 服务注册 服务提供者在启动时&#xff0c;需要将其服务信…

Prometheus的infratest、UAT、PRE、PRD分别代表什么

Prometheus的infratest、UAT、PRE、PRD分别代表什么 在Prometheus监控系统中,infratest、UAT、PRE和PRD通常指的是不同阶段的测试环境,分别对应基础设施测试(Infrastructure Test)、用户验收测试(User Acceptance Test)、预生产环境(Pre-production)和生产环境(Produ…

构建RISC-V工具链:基本步骤

在这一节内容中&#xff0c;我们将介绍如何构建一个64位的RISC-V工具链。在这个过程中&#xff0c;我们将编译默认的RISC-V工具链&#xff0c;而不修改指令集。 1. 安装必要的软件包 首先&#xff0c;需要安装一些必要的软件包。在终端中运行以下命令&#xff1a; sudo apt-g…

vue3-cropperjs图片裁剪工具-用户上传图片截取-(含预览视频)

效果图 上传图片弹窗预览 对于这个上传图片样式可以参考 官方原代码 官网传送入口 Upload 上传 | Element Plus (element-plus.org) <template><el-uploadclass"upload-demo"dragaction"https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6…

免费分享一套SpringBoot+Vue电影院售票管理系统【论文+源码+SQL脚本】,帅呆了~~

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringBootVue电影院售票管理系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringBootVue电影院售票管理系统 Java毕业设计_哔哩哔哩_bilibili【免费】SpringBootVue电影院售票管理系统 Java毕业设计…

DriverManager.getConnection用法总结

DriverManager.getConnection用法总结 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;DriverManager.getConnection是一个用于建立与…

《Windows API每日一练》5.5 插入符号

当你向程序中输入文本时&#xff0c;通常会有下划线、竖线或方框指示你输入的下一个字符将出现在屏幕上的位置。你也许认为这是“光标”&#xff0c;但在编写Windows程序时&#xff0c;你必须避免这种习惯。在Windows中&#xff0c;它被称为“插入符号”&#xff08;caret&…