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

C--结构体和位段的使用方法

各位看官如果您觉得这篇文章对您有帮助的话
欢迎您分享给更多人哦

请添加图片描述

感谢大家的点赞收藏评论,感谢您的支持!!!

一:结构体

首先结构体我们有一个非常重要的规则 非常重要:

我们允许在初始化时自动将字符串字面量复制到字符数组中,但这不是通过赋值操作完成的,而是在初始化时直接进行的。然而,这要求整个初始化表达式是在结构体初始化列表中,而不是在结构体定义之后作为赋值操作。

就是说在结构体初始化之后s1.age=20可以,但是s1.name="abcdef"不行

#include <stdio.h>  
#include <string.h>  struct stu  
{  char name[20];  int age;  char sex;  
};  int main()  
{  struct stu s1 = { .name = "lisi", .age = 18, .sex = 'F' };  // 修改字符数组类型的成员  strcpy(s1.name, "abcdef");  但是s.name="abcdef"不合法// 打印修改后的结构体成员以验证  printf("Name: %s, Age: %d, Sex: %c\n", s1.name, s1.age, s1.sex);  return 0;  
}

在C语言中,结构体初始化之后,对于非数组(或更具体地说,非字符数组)类型的成员,如 int 或 char,您可以直接使用赋值操作符(=)来修改它们的值。这是为什么呢?

因此,s1.age = 20; 是完全合法的。 然而,对于字符数组(如 char name[20];),情况就不同了。您不能直接使用赋值操作符将一个新的字符串字面量(如 “abcdef”)赋给字符数组,因为字符串字面量在内存中是一个常量,而数组名在大多数情况下表示数组首元素的地址(尽管它本身不是一个左值,但在初始化时是一个例外)。
如果您想在结构体初始化之后修改字符数组类型的成员,您需要使用字符串处理函数,如 strcpy

在大多数情况下(特别是在赋值操作中),你不能将数组名当作左值来使用,相当于直接arr=“abcdef”(错误)因为它不代表一个可以存储新值的变量。这就是为什么你不能写s1.name = “abcdef”;这样的代码来修改字符数组的原因。相反,你需要使用strcpy这样的函数来复制字符串到数组中。

总结:
对于非数组类型的结构体成员,可以直接使用赋值操作符(=)来修改它们的值。
对于字符数组类型的结构体成员,需要使用字符串处理函数(如 strcpy)来修改它们的值。

1: 结构体的特殊声明

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/40d507c992ca4bb086f69a6b5fbdb3c0.jpeg

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

2:结构体的自引用

在结构中包含⼀个类型为该结构本⾝的成员是否可以呢?(结构体的自引用)
⽐如,定义⼀个链表的节点

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

上述代码正确吗?如果正确,那 sizeof(struct Node) 是多少?
仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会⽆穷的大,是不合理的。(一直引用无穷无尽)

正确方法:这里我放一个指针:我就可以不去解引用,这样就不会一直访问了,或者置为NULL

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

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

typedef struct
{int data;Node* next;   **还没重新命名你就用上了?**
}Node;  

所以说那样不行:这样就正确了,要有名字,然后用重命名之前的类型名字

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

3:结构体的对齐规则

3.1:首先掌握对齐规则

  1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值。
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对齐数,对齐数就是成员自身的大小
  1. 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最⼤的)的
    整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最⼤对齐数的整数倍处,结构
    体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。
#include <stdio.h>
int main()
{// 练习1struct S1{char c1;int i;char c2;};printf("%d\n", sizeof(struct S1)); 12//练习2struct S2{char c1;char c2;int i;};printf("%d\n", sizeof(struct S2));8//练习3struct S3{double d;char c;int i;};printf("%d\n", sizeof(struct S3));16//练习4-结构体嵌套问题struct S4{char c1;struct S3 s3;double d;};printf("%d\n", sizeof(struct S4));32return 0;
}

3.2 为什么存在内存对齐?

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

    总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在⼀起

1 //例如:
2 struct S1
3 {
4 char c1;
5 int i;
6 char c2;
7 };
8
9 struct S2
10 {
11 char c1;
12 char c2;
13 int i;
14 };
S1 和 S2 类型的成员⼀模⼀样,但是 S1 和 S2 所占空间的⼤⼩有了⼀些区别。

3.3:修改默认对齐数

默认对齐数的大小:一般为2^n(1,2,4,8,……)

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1,一般为2^n(1,2,4,8……)
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S));默认对齐数为1时候的值(6return 0;
}

4:结构体传参(尽量传地址)

struct S
{int data[1000];int num;
};
struct S s = {{1,2,3,4}, 1000};
//结构体传参
void print1(struct S s) 传值:再传date[1000],形参太大,时间太长,再压栈一大堆
{printf("%d\n", s.num);
}
//结构体地址传参
void print2(struct S* ps)
{printf("%d\n", ps->num);
}
int main()
{
print1(s); 传结构体
print2(&s);传指针

上⾯的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。

原因:
函数传参的时候,参数是需要压栈(push进去,出来再销毁,费劲),会有时间和空间上的系统开销。 如果传递⼀个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。 结论:
结构体传参的时候,要传结构体的地址。

5:位段

  1. 位段的成员必须是 int、unsigned int 或signed int 或者是char类型,在C99中位段成员的类型也可以
    选择其他类型。
  2. 位段的成员名后边有⼀个冒号和⼀个数字。
#include <stdio.h>
struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{printf("%d\n", sizeof(struct A));8return 0;
}

5.1 位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
    4
    在这里插入图片描述

位段的问题:
6. int 位段被当成有符号数还是⽆符号数是不确定的。7. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题。
8. 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。(我们看来是左向右)
9. 当⼀个结构包含两个位段,第⼆个位段成员比较大,无法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利用,这是不确定的。(我们看来是舍弃)
总结:
跟结构相比较,位段(只能是int,unsigned int,char)可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

5.2:

像这样的传输信息采用位段就可以减少空间的使用,避免网络拥挤(打包数量小)

在这里插入图片描述

6:位段的注意事项:

位段的几个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位
置处是没有地址的
譬如char a : 3; char b : 4; ( b就没有地址)
内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使用&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊
放在⼀个变量中,然后赋值给位段的成员

struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{struct A sa = {0};scanf("%d", &sa._b);这是错误的正确的⽰范int b = 0;scanf("%d", &b);sa._b = b;先输⼊
放在⼀个变量中,然后赋值给位段的成员return 0;
}

上述就是C–结构体和位段的使用方法的内容了
能看到这里相信您一定对小编的文章有了一定的认可)
有什么问题欢迎各位大佬指出!!!

请添加图片描述
欢迎各位大佬评论区留言修正 !!!

请添加图片描述

相关文章:

C--结构体和位段的使用方法

各位看官如果您觉得这篇文章对您有帮助的话 欢迎您分享给更多人哦 感谢大家的点赞收藏评论&#xff0c;感谢您的支持&#xff01;&#xff01;&#xff01; 一&#xff1a;结构体 首先结构体我们有一个非常重要的规则 非常重要&#xff1a; 我们允许在初始化时自动将字符串字面…...

卷积神经网络-迁移学习

文章目录 一、迁移学习1.定义与性质2.步骤 二、Batch Normalization&#xff08;批次归一化&#xff09;三、ResNet网络1.核心思想2.残差结构&#xff08;1&#xff09;残差块&#xff08;2&#xff09;残差结构类型 四、总结 一、迁移学习 迁移学习&#xff08;Transfer Lear…...

数据库:PL/SQL

变量 变量 建议使用V开头 作用: 用来保存一个数据 普通变量 declare --定义一个变量&#xff08;保存一个数据&#xff09; v_email varchar2(20); --定义变量并且赋值 v_ename varchar2(20) :张三; beginv_email :553215qq.com;dbms_output.put_line(v_email);dbms_output…...

迅雷笔试 最长相等子段数列长度 滑动窗口

&#x1f468;‍&#x1f3eb; 牛马Code&#xff1a;最长相等子段数列长度 import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Arrays; import java.util.HashMap;public class Main {// 创建一个输入流读取器&#xff0c;用于读取控制台输…...

MMD模型一键完美导入UE5-VRM4U插件方案(一)

1、下载pmx模型 1、去模之屋官网下载MMD模型,模之屋 2、下载完成得到pmx和Texture文件 2、下载并启用VRM4U插件 1、下载VRM4U插件, VRM4U,点击Latest下载对应引擎版本 2、将插件放到Plugins目录,然后...

bench.sh:一行命令测试Linux服务器基准测试

简介 bench.sh 是一个 Linux 系统性能基准测试工具。它的测试结果如下图&#xff1a;给出服务器的整体配置信息&#xff0c;IO 性能&#xff0c;网络性能。很多人使用它测试 vps 性能。 ​​ 一键运行 服务器在国外可以使用以下命令运行测试 wget -qO- bench.sh | bash复制…...

广播IP与共享IP的关系

什么是广播IP&#xff1f; 广播IP是指一种特殊的IP地址&#xff0c;用于在网络中向所有设备发送信息。广播地址通常是某个网络段的最后一个地址&#xff0c;例如&#xff0c;在一个子网掩码为255.255.255.0的网络中&#xff08;即/24子网&#xff09;&#xff0c;其网络地址可…...

正则表达式和re模块

正则表达式&#xff08;Regular Expression&#xff0c;简称Regex或RegExp&#xff09;是计算机科学中的一个重要概念&#xff0c;它通常被用来检索、替换那些符合某个模式&#xff08;规则&#xff09;的文本。正则表达式是对字符串操作的一种逻辑公式&#xff0c;通过事先定义…...

不同的浏览器、服务器和规范对 URL 长度的限制

不同的浏览器、服务器和规范对 URL 长度的限制有所不同。通常的限制如下&#xff1a; 1. 浏览器限制&#xff1a; 常见浏览器对 URL 长度的限制在 2,000 到 8,000 个字符之间。例如&#xff1a; Internet Explorer: 大约 2,083 个字符。Google Chrome: 理论上支持超过 32,00…...

NASA:ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006数据集

目录 简介 代码 引用 网址推荐 0代码在线构建地图应用 机器学习 ATLAS/ICESat-2 L3A Along Track Inland Surface Water Data V006 简介 ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006 ATLAS/ICESat-2 L3 A沿线内陆地表水数据V006是指由ATLAS/ICESat-2卫星获取的针对陆地…...

数据结构之链表(1),单链表

目录 前言 一、什么是链表 二、链表的分类 三、单链表 四、单链表的实现 五、SList.c文件完整代码 六、使用演示 总结 前言 本文讲述了什么是链表&#xff0c;以及实现了完整的单链表。 ❤️感谢支持&#xff0c;点赞关注不迷路❤️ 一、什么是链表 1.概念 概念&#xff1a;链…...

如何构建鲁棒高性能 Prompt 的方法?

你好&#xff0c;我是三桥君 在当今时代&#xff0c;利用大型语言模型如ChatGPT进行文本生成和交互已成为一种趋势。然而&#xff0c;要充分发挥这些模型的能力&#xff0c;尤其是在生产环境中&#xff0c;我们需要精心设计和优化我们的提示词&#xff08;prompt&#xff09;。…...

基于Springboot+微信小程序 的高校社团管理小程序(含源码+数据库+lw)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 系统定…...

Vue 响应式监听 Watch 最佳实践

一. 前言 上一篇文章我们学习了 watch 的基础知识&#xff0c;了解了它的基本使用方法及注意事项&#xff0c;本篇文章我们继续了解在Vue 中 响应式监听 watch 的妙用。了解 watch 的基础使用请参考上一篇文章&#xff1a; 详解 Vue 中 Watch 的使用方法及注意事项https://bl…...

md编辑器语法

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...

RabbitMQ常用管理命令及管理后台

RabbitMQ管理命令 1、用户管理1.1、新增一个用户1.2、查看当前用户列表1.3、设置用户角色1.4、设置用户权限1.5、查看用户权限 2、RabbitMQ的web管理后台2.1、查看rabbitmq 的插件列表2.2、启用插件2.3、禁用插件2.4、访问RabbitMQ的web后台2.4、通过web页面新建虚拟主机 ./rab…...

从准备面试八股文,感悟到技术的本质

工作前几年听说过&#xff0c;大学最重要的几门课其实是数据结构和算法、操作系统、计算机组成原理、计算机网络。 初听时不以为然&#xff0c;感觉没什么用。 近期准备面试八股文得到了一些感悟。这句话随着工作年限和对程序的理解越来越深入&#xff0c;含金量越来越高。 最…...

云手机的默认ip地址是什么

云手机&#xff08;Cloud Phone&#xff09;是一种基于云计算技术的虚拟手机&#xff0c;它可以在云端运行&#xff0c;使用户能够通过互联网访问手机应用和服务。云手机的IP地址通常取决于以下几个因素&#xff1a; 1. 云服务提供商 不同的云服务提供商&#xff08;如AWS、G…...

对接阿里asr和Azure asr

1&#xff1a;对接阿里asr 1.1&#xff1a;pom <dependency><groupId>com.alibaba.nls</groupId><artifactId>nls-sdk-recognizer</artifactId><version>2.2.1</version> </dependency>1.2&#xff1a;生成token package c…...

未来数字世界相关技术、应用:AR/VR/MR;数字人、元宇宙、全息显示

一、AR/VR/MR 增强现实(AR)、虚拟现实(VR)和混合现实(MR)是三种不同的技术,它们都旨在增强用户对现实世界的感知和交互体验。以下是它们的详细介绍: 增强现实(AR) 增强现实(Augmented Reality, AR) 是一种将虚拟信息叠加到现实世界中的技术。通过AR技术,用户可…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

视觉slam十四讲实践部分记录——ch2、ch3

ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

OD 算法题 B卷【正整数到Excel编号之间的转换】

文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的&#xff1a;a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...