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

c语言进阶部分详解(详细解析自定义类型——结构体,内存对齐,位段)

上篇文章介绍了一些常用的字符串函数,大家可以去我的主页进行浏览。

各种源码大家可以去我的github主页进行查找:Nerosts/just-a-try: 学习c语言的过程、真 (github.com)  

今天要介绍的是:结构体的相关内容


目录

一.结构体类型的声明

1.结构的基础知识

2.结构的声明 

3.特殊的声明 

4.结构的自引用

5.结构体变量的定义和初始化 

 6.结构体传参

 二.结构体内存对齐  

 1.对其规则

2.存在原因

3.减少浪费 

​编辑

4.修改默认对齐数

三.位段 

1.什么是位段

2.位段的内存分配

3.位段的跨平台问题 


一.结构体类型的声明

1.结构的基础知识

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

2.结构的声明 

 结构的声明的原型:

struct tag
{
    member - list ;
} variable - list ;

eg: 

struct Student
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号
}; //分号不能丢

3.特殊的声明 

匿名结构体: 

struct
{int a;char b;float c;
}x;struct
{int a;char b;float c;
}a[20], *p;

4.结构的自引用

 我们也许回想在结构中包含一个类型为该结构本身的成员是否可以呢?像下面这样

struct Node
{struct Node a1;int date[2];
};

这种情况下,需要确保结构体类型的定义是在使用它的结构体类型之前。否则,编译器将无法确定结构体类型的大小。

所以是不行的,正确的自引用方式如下:

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

注意:

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

在定义指针变量next时,使用了Node类型。由于Node类型的定义在当前代码中尚未完成,编译器无法识别Node类型

正确的如下:

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

5.结构体变量的定义和初始化 

struct Point
{int x;int y;
}p1; //声明结构体的同时定义变量p1struct Point p2; //定义结构体变量p2
//也可以
struct Point p3 = {x, y};//初始化:定义变量的同时赋初值。struct Stu        //类型声明
{char name[15];//名字int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{int data;struct Point p;struct Node* next; 
}n1 = {10, {4,5}, NULL}; //声明结构体的同时嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//单独结构体嵌套初始化

 6.结构体传参

struct S
{int data[1000];int num;
};
struct S s = { {1,2,3,4}, 1000 };
//结构体传参
void print1(struct S s)
{printf("%d\n", s.num);printf("%d\n", s.data[0]);printf("%d\n", s.data[1]);printf("%d\n", s.data[2]);
}
//结构体地址传参
void print2(struct S* ps)
{printf("%d\n", ps->num);printf("%d\n", ps->data[0]);printf("%d\n", ps->data[1]);printf("%d\n", ps->data[2]);
}
int main()
{print1(s); //传结构体变量printf("_________");print2(&s); //传地址return 0;
}

两种调用,传参的结果都是一样的:

 但是,还是用printf2来传地址时更好:

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

 二.结构体内存对齐  

struct S1
{
     char c1 ;
     int i ;
     char c2 ;
};     这个结构体有多大呢? 
第一反应大抵是:1+4+1=6吧

 但其实:

 

 1.对其规则

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

 按照对其规则,这是上述答案的分析:

接下来再看另外一个例子: 

eg:

struct S2
{char c1;char c2;int i;
};int main()
{printf("%d", sizeof(struct S2));return 0;
}

答案便是:

2.存在原因

结构体的内存对齐是拿空间来换取时间的做法

一些资料也显示说:

  1. 访问效率:内存对齐可以使结构体的成员在内存中连续存储,减少内存访问次数,提高访问效率。如果结构体成员没有进行内存对齐,可能会导致成员之间存在空隙,需要多次访问内存才能获取到所有成员的值
  2. 数据对齐:某些硬件平台要求访问特定类型的数据必须按照特定字节对齐,否则可能会导致访问错误或性能下降。例如,某些处理器要求访问双字节数据(如short类型)必须按2字节对齐,访问四字节数据(如int类型)必须按4字节对齐。如果结构体成员没有进行内存对齐,可能会导致访问错误或性能下降

 

3.减少浪费 

那我们如何尽量减少内存对齐所产生的浪费呢?

让占用 空间小的成员尽量集中 在一起

所以上述S1的例子我们可以改造成:

struct S1
{char c1;int i;char c2;
};
struct S2
{char c1;char c2;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d", sizeof(struct S2));return 0;
}

结果也确实减小了内存使用:

4.修改默认对齐数

 这里我们使用#pragma,可以改变我们的默认对齐数

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

这次S2的大小便是我们最初认为的6了:


三.位段 

1.什么是位段

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

  1. 位段的成员必须是 intunsigned int signed int
  2. 位段的成员名后边有一个冒号和一个数字
struct A {int a : 2; //a占用2个bit位int b : 5; //b占用5个bit位int c : 10;int d : 30;
};int main()
{struct A a;printf("%d", sizeof(a));
}

不使用位段的话是占16个字节,现在是:

 

 

2.位段的内存分配

  1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的(一次增加4/1个字节)
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

 

struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}

3.位段的跨平台问题 

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大1632位机器最大32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定

    这次关于结构体相关的内容就先到这里啦!                                                                           在下一篇文章中,我们将详细介绍枚举和联合体的内容。感谢大家的支持,加油!!!

 

相关文章:

c语言进阶部分详解(详细解析自定义类型——结构体,内存对齐,位段)

上篇文章介绍了一些常用的字符串函数,大家可以去我的主页进行浏览。 各种源码大家可以去我的github主页进行查找:Nerosts/just-a-try: 学习c语言的过程、真 (github.com) 今天要介绍的是:结构体的相关内容 目录 一.结构体类型的声明 1.…...

Mysql第三篇---响应太慢?数据库卡顿?如何优化?

Mysql第三篇—响应太慢?数据库卡顿?如何优化? 统计SQL的查询成本:last_query_cost 一条SQL查询语句在执行前需要确定查询执行计划,如果存在多种执行计划的话,MySQL会计算每个执行计划所需要的成本&#x…...

【计算机网络】HTTP 协议的基本格式以及 fiddler 的用法

HTTP协议的基本格式如下: 1.请求行: 包括请求THHP协议的版本、请求URI(资源路径)和HTTP方法(如GET、POST、PUT、DELETE等) GET/example.html HTTP/1.1 GET表示请求方法,/example.html表示请求的…...

人大金仓与哪吒科技达成战略合作,加快推动智慧港口建设

近日,人大金仓与哪吒港航智慧科技(上海)有限公司(以下简称“哪吒科技”)达成战略合作。双方旨在共享优势资源,联合为港口企业转型升级提供完备的技术支撑与行业解决方案。人大金仓总裁杜胜、哪吒科技总经理…...

FFmpeg工具使用集

FFmpeg工具使用集 About FFmpeg Java调用FFmpeg FFmpeg 工具: FFMPEG 用于转换多媒体文件的 命令行工具 格式之间( ffmpeg\bin\ffmpeg.exe ) ffplay 基于 SDL 和 FFmpeg 库的简单媒体播放器 ( ffmpeg\bin\ffplay.exe &#xff0…...

2024级199管理类联考之英语二2200核心词汇(第一天)

define 下定义,定范围definition 定义,清晰度identify 鉴定,识别,确认identifiable 可识别的,可辨认的identity 身份,一致determine 决心,确定determinism 宿命论,决定论judge 判断,法官/裁判behavior 举止,表现behavioral 行为的conduct v-实施,引导,指挥; n-实施方式,行为,举…...

webGL编程指南 第三章 平移三角形 TranslatedTriangle.js

我会持续更新关于wegl的编程指南中的代码。 当前的代码不会使用书中的缩写&#xff0c;每一步都是会展开写。希望能给后来学习的一些帮助 git代码地址 接着 上一节 接着做平移的转化。在本次的案例案例中主要是xy的坐标变量相加&#xff0c;同时传递个给相关变量 <!DOCTY…...

推荐一款支持异步批量下载图片的chrome插件——图片助手(ImageAssistant) 批量图片下载器

https://chrome.google.com/webstore/detail/imageassistant-batch-imag/dbjbempljhcmhlfpfacalomonjpalpko/related?hlzh-CNhttps://chrome.google.com/webstore/detail/imageassistant-batch-imag/dbjbempljhcmhlfpfacalomonjpalpko/related?hlzh-CN 安装后直接点击 会根据…...

vue 动态数字效果 vue-animate-number

安装 vue-animate-number 插件 npm install vue-animate-number &#xff08;注&#xff1a;是npm、cnpm还是yarn根据具体项目要求&#xff09; 在 main.js 中引入 import Vue from vue import VueAnimateNumber from vue-animate-number Vue.use(VueAnimateNumber)动态使用…...

10月22日,每日信息差

今天是2023年10月22日&#xff0c;以下是为您准备的13条信息差 第一、库迪咖啡计划到2025年底全球门店数量达2万家&#xff0c;库迪咖啡开业一周年全球门店数量达到6061家&#xff0c;位居全球第四 第二、超高速纯硅调制器取得创纪录突破&#xff0c;国际上首次把纯硅调制器带…...

Android系统之SurfaceFlinger

参考资料&#xff1a; Android 显示系统&#xff1a;SurfaceFlinger详解 Android 渲染机制——SurfaceFlinger 一篇文章看明白 Android 图形系统 Surface 与 SurfaceFlinger 之间的关系 Android卡顿原理分析和SurfaceFlinger&#xff0c;Surface概念简述 Android Graphics…...

jQuery实现输入框提示并点击回显功能呢

html代码: <input type"text" id"affOrganization" name"affOrganization" class"form-control" placeholder"Search..." style"width: 300px" > <div class"search_suggest" id"gov_se…...

终端常用操作

终端操作 取消 可以用ctrl c&#xff0c;不要一个一个删除&#xff0c;会取消掉开新的一行回溯上一次的命令&#xff0c;ctrl r&#xff0c;然后键入关键词&#xff0c;直接回车运行就行 chmod x 文件名 给某个文件运行需要的权限。...

JWFD开源工作流矩阵引擎测试版本BUG20231022修正代码

public void ParamFileOutputValue(String paramfile) {String s "";String sp "";String ssp "";List<String> list new ArrayList<String>();int p 0;int k 0;//这个地方要修改为整个参数表的最大行数&#xff0c;而不是起始…...

分拣设备运动仿真

这一次我们来分享一下如何在Solidworks 中做出传送台的分拣动作并通过分拣动作生成过程动画&#xff0c;以便于我们可以用于产品展示又或者验证运行程序无误的情况下结构是否会影响输送效率。 首先创建一个新的运动算例 将窗口切换至Motion分析 在设置之前我们先理清设置传送带…...

Python【列表的反转与排序】

目录 要求&#xff1a;列表的反转 列表的排序 列表的反转&#xff1a; 方案一&#xff1a;使用reverse()方法&#xff1a;它会直接修改原始列表&#xff0c;进行反转。 方案二&#xff1a;还是使用reversed()函数&#xff1a;该函数返回一个反转后的迭代器对象&#xff0c…...

2 用TensorFlow构建一个简单的神经网络

上一篇&#xff1a;1 如何入门TensorFlow-CSDN博客 1、环境搭建 后续介绍的相关代码都是在pycharm运行&#xff0c;pycharm安装略。 打开pycharm&#xff0c;创建一个新的项目用于tensorflow编码练习&#xff0c;在Terminal输入命令&#xff1a; # 依赖最新版本的pip pip i…...

快手视频批量下载.py(10月可以用)

完整代码如下: # _*_ coding:utf-8 _*_# _*_ coding:utf-8 _*_import json import requests import time import randomheaders = """Accept: */* Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 Con…...

如何用工业树莓派和MQTT平台打通OT和IT?

一、应用设备 OT端设备&#xff1a;步进电机&#xff0c;MODBUS TCP远程I/O模块&#xff0c;PLC设备 边缘侧设备&#xff1a;宏集工业树莓派&#xff1b; IT端设备&#xff1a;PC、安卓手机&#xff1b; IT端软件&#xff1a;宏集HiveMQ MQTT通信平台 二、原理 宏集工业树…...

大模型背景下软件工程的机遇与挑战

点击链接了解详情 本文作者&#xff1a;汪晟杰 导语:AISE&#xff08;AI Software Engineering&#xff09;有人说是软件工程 3.0&#xff0c;即基于大模型&#xff08;LLM - Large Language Model&#xff09;时代下的软件工程。那么究竟什么是 AISE&#xff0c;他的发展历程对…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Spring是如何解决Bean的循环依赖:三级缓存机制

1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间‌互相持有对方引用‌,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...