深入浅出变长结构体

news2024/12/23 5:29:08

1、 问题的引出

        项目中用到数据包的处理,但包的大小是不固定的,其长度由包头的2字节决定。比如如下的包头:88 0f 0a ob cd ef 23 00 。长度由头2个字节880f决定,考虑字节序,转为0f88,转为10进制3976个字节的包长度。

        这个时候存储包的时候,一方面可以考虑设定包的大小固定:如4K=4*1024=4096个字节,因为最大包长不可能超过4k,但该方法的有缺陷,存在一种极端就是包最小仅含包头不含数据域,此时包为8个字节,浪费了4096-8 =4088个字节的存储空间。另一方面考虑有没有一种方法能根据长度进行存储,或者说初始不分配长度,计算出了长度后再分配存储呢。而实际项目中正是通过包头计算出了包的整体大小的。

        这就引出了变长结构体的概念。

2、 什么叫变长结构体?

     如下所示:

  1. struct Var_Len_Struct  
  2. {  
  3.      int nsize;  
  4.      char buffer[0];  
  5. };  

        那结构体是怎么实现可变长的呢?如上所示,请注意看结构体中的最后一个元素,一个没有元素的数组。我们可以通过动态开辟一个比结构体大的空间,然后让buffer去指向那些额外的空间,这样就可以实现可变长的结构体了。更为巧妙的是,我们甚至可以用nsize存储字符串buffer的长度。

       并且,上述的结构体可以扩展,比如笔者项目中遇到的存储数据包,前面可能类似包头的部分(存储类型、长度等信息),而后面buffer则存储数据部分。

       同时,需要引起注意的:ISO/IEC 9899-1999里面,这么写是非法的,这个仅仅是GNU C的扩展,gcc可以允许这一语法现象的存在。但最新的C/C++不知道是否可以,我没有测试过。C99允许。

3、变长结构体的好处体现在哪?

        可能有的同学会问到,1引出部分如果说定义定长数组浪费空间,定义一个指针不也能指向变长的数据域部分吗?

       是的,是可以实现的。那么我们就对比下有什么不同。

       结构体1:s_one,用指针指向数据域部分;

       结构体2:s_two, 用[0]的数组;

       结构体3:s_three, 因为有的编译器不支持[0],我们用[1]来表示;多了些存储。

  1. #include <stdafx.h>  
  2. #include <iostream>  
  3. using namespace std;  
  4.    
  5. const int BUF_SIZE = 100;  
  6.    
  7. struct s_one  
  8. {  
  9. ints_one_cnt;  
  10. char*s_one_buf;  
  11. };  
  12.    
  13. struct s_two  
  14. {  
  15. ints_two_cnt;  
  16. chars_two_buf[0];  
  17. };  
  18.    
  19. struct s_three  
  20. {  
  21. ints_three_cnt;  
  22. chars_three_buf[1];  
  23. };  
  24.    
  25. int main()  
  26. {  
  27. //赋值用  
  28. constchar* tmp_buf = "abcdefghijklmnopqrstuvwxyz";  
  29. intntmp_buf_size = strlen(tmp_buf);  
  30.    
  31. //<1>注意s_one 与s_two的大小的不同  
  32. cout<< "sizeof(s_one) = " << sizeof(s_one) << endl; //8  
  33. cout<< "sizeof(s_two) = " << sizeof(s_two) << endl; //4  
  34. cout<< "sizeof(s_three) = " << sizeof(s_three) << endl;//5-->8结构体对齐  
  35. cout<< endl;  
  36.    
  37. //为buf分配100个字节大小的空间  
  38. intntotal_stwo_len = sizeof(s_two) + (1 + ntmp_buf_size) * sizeof(char);  
  39. intntotal_sthree_len = sizeof(s_three) + ntmp_buf_size * sizeof(char);  
  40.    
  41. //给s_one buf赋值  
  42. s_one*p_sone = (s_one*)malloc(sizeof(s_one));  
  43. memset(p_sone,0, sizeof(s_one));  
  44. p_sone->s_one_buf= (char*)malloc(1 + ntmp_buf_size);  
  45. memset(p_sone->s_one_buf,0, 1 + ntmp_buf_size);  
  46. memcpy(p_sone->s_one_buf,tmp_buf, ntmp_buf_size);  
  47.    
  48. //给s_two buf赋值  
  49. s_two*p_stwo = (s_two*)malloc(ntotal_stwo_len);  
  50. memset(p_stwo,0, ntotal_stwo_len);  
  51. memcpy((char*)(p_stwo->s_two_buf),tmp_buf, ntmp_buf_size);  //不用加偏移量,直接拷贝!  
  52.    
  53. //给s_three_buf赋值  
  54. s_three*p_sthree = (s_three*)malloc(ntotal_sthree_len);  
  55. memset(p_sthree,0, ntotal_sthree_len);  
  56. memcpy((char*)(p_sthree->s_three_buf),tmp_buf, ntmp_buf_size);  
  57.    
  58. cout<< "p_sone->s_one_buf = " << p_sone->s_one_buf<< endl;  
  59. cout<< "p_stwo->s_two_buf = " << p_stwo->s_two_buf<< endl;  
  60. cout<< "p_sthree->s_three_buf = " <<p_sthree->s_three_buf << endl; //不用加偏移量,直接拷贝!  
  61. cout<< endl;  
  62.    
  63. //<2>注意s_one 与s_two释放的不同!  
  64. if(NULL != p_sone->s_one_buf)  
  65. {  
  66.         free(p_sone->s_one_buf);  
  67.         p_sone->s_one_buf= NULL;  
  68.    
  69.         if(NULL != p_sone)  
  70.         {  
  71.                free(p_sone);  
  72.                p_sone= NULL;  
  73.         }  
  74.         cout<< "free(p_sone) successed!" << endl;  
  75. }  
  76.    
  77. if(NULL != p_stwo)  
  78. {  
  79.         free(p_stwo);  
  80.         p_stwo= NULL;  
  81.    
  82.         cout<< "free(p_stwo) successed!" << endl;  
  83. }  
  84.    
  85. if(NULL != p_sthree)  
  86. {  
  87.         free(p_sthree);  
  88.         p_sthree= NULL;  
  89.    
  90.         cout<< "free(p_sthree) successed!" << endl;  
  91. }  
  92.    
  93. return0;  
  94. }  

       笔者vc6.0的编译器会有如下的警告:

 

       运行结果如下:

       对比结果,我们能发现:

       <1> 存储大小方面:s_two的存储较s_one、s_three都要少,[0]的好处,即用指针的方式需要多开辟存储空间的。

       <2> 数据连续存储方面:s_one明显数据域是单独开辟的空间,与前的nsize不在连续的存储区域,而s_two,s_three则在连续的存储空间下。

       <3>释放内存方面:显然s_one的指针的方式,需要先释放数据域部分,才能释放指向结构体的指针变量;而s_two,s_three可以直接释放。

       总结如下:

       结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所以要分别管理即申请和释放)。

      而如果使用数组,那么只需要一次就可以全部分配出来,反过来,释放时也是一样,使用数组,一次释放,使用指针,得先释放结构体内的指针,再释放结构体。还不能颠倒次序。

      其实变长结构体就是分配一段连续的的内存,减少内存的碎片化,简化内存的管理。

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

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

相关文章

汽车零部件行业追溯系统的应用

汽车行业正处于一个蓬勃发展的阶段&#xff0c;随着客户需求的不断变化&#xff0c;生产厂商推出新款商品的速度也越来越快&#xff0c;新项目和变更的不断出现&#xff0c;就可能导致在交付的产品质量方面遇到各种各样的问题。如果这些质量问题得不到及时有效地追溯和控制&…

知识点滴 - PLC培训笔记

如上图所示&#xff0c;这个设备就是PLC的应用场景&#xff0c;对外控制很多接口电路&#xff0c;交流直流&#xff0c;不同大小电流、电压的电路。在工业上使用很多。 而开发PLC程序时&#xff0c;是在相应的集成开发环境中&#xff0c;画出如下的梯形图&#xff1a; 什么是PL…

Vue使用Element-UI生成并展示表头序号

前言 序号算是在展示数据的时候&#xff0c;一种很普遍的属性了&#xff0c;我们可以自己写生成序号的规则&#xff0c;也可以借助第三方。 这里我选择后者&#xff0c;使用Element-UI自带的一个table属性&#xff0c;去生成并展示需要 首先看下我的页面&#xff0c;是没有序…

Apache Sling App CMS <1.1.4 存在反射型XSS漏洞(CVE-2022-46769)

漏洞描述 Apache Sling 是一个基于可扩展内容树&#xff08;extensible content tree&#xff09;的 RESTful Web 应用框架。 1.1.4 之前版本的 Apache Sling 中的 cms.js#confirmMessage 方法未对用户可控的 title 和 message 参数进行过滤&#xff0c;攻击者可将精心构造的…

dubbo源码实践-总结

自己大概花了一个月的时间&#xff0c;断断续续的看了一遍dubbo源码&#xff0c;之前的文章从实践出发搭建了dubbo各层的例子&#xff01;Dubbo源码的学习也暂时告一段落。这篇就谈谈自己对dubbo源码学习个人感受&#xff01;1 dubbo是什么&#xff1f;dubbo是一个RPC框架&…

网站优化包括对什么优化(网站优化有哪些方面)

互联网时代下&#xff0c;各行各业都想在在互联网中占据市场&#xff0c;目前网站优化成为时代的潮流&#xff0c;很多站长&#xff0c;在对网站进行优化时&#xff0c;稍不注意就会造成网站优化过度&#xff0c;这也是各位最头疼的事情&#xff0c;网站优化能急于求成&#xf…

KubePi <1.6.4 存在会话固定漏洞(CVE-2023-22479)

漏洞描述 KubePi 是一个 K8s 面板&#xff0c;允许管理员导入多个 Kubernetes 集群&#xff0c;并且通过权限控制将不同 cluster、namespace 的权限分配给指定用户。 1.6.4 之前版本的 KubePi 在用户登录后未刷新用户 cookie&#xff0c;攻击者可通过诱导用户点击恶意链接等劫…

maven创建web工程,使用模板方式(二)

一&#xff0c;选择webapp模板 二&#xff0c;填写完信息后&#xff0c;创建后是如下的结构&#xff1a; 可以看到只有web目录&#xff0c;没有其他的java&#xff0c;resources&#xff0c;test目录等。 三&#xff0c;创建Java&#xff0c;resources,test目录 右键-> ma…

【C语言进阶】 指针强化练习

目录题目一题目二题目三题目四题目五题目六题目七题目八题目一 下面这段代码的执行结果是&#xff1f; int main() {int a[5] { 1, 2, 3, 4, 5 };int* ptr (int*)(&a 1);printf("%d,%d\n", *(a 1), *(ptr - 1));return 0; }解决这种数组与指针相结合的问题…

【Leetcode面试常见题目题解】2. 无重复字符的最长子串

题目描述 本文是LC第3题&#xff1a;无重复字符的最长子串。 题目描述如下 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 举例 输入: s “abcabcbb” 输出: 3 解释: 因为无重复字符的最长子串是 “abc”&#xff0c;所以其长度为 3。 输入:…

html实现酷炫的公司年会抽奖(附源码)

文章目录1.设计来源1.1 主界面1.2 抽奖效果1.2 中奖效果2.效果和源码配置2.1 动态效果2.2 员工信息配置2.3 奖品信息配置2.4 抽奖音效配置2.5 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/128640998 ht…

AI绘画日漫欧漫动态表情换脸游戏风生成红包封面流量主小程序开发

AI绘画日漫欧漫动态表情换脸游戏风生成红包封面流量主小程序开发 因全网AI绘画大量小程序被封禁下架。只有采用合规稳定运营引擎。 文生图图动日漫图生欧漫图转动图人物表情变脸换脸3D动漫集成7大AI制图模式 支持个人模式和企业支付。趣味AI制图支持流量主。 打造一款适合个人…

uniapp 画中画悬浮窗(视频) Ba-VideoPip

简介&#xff08;下载地址&#xff09; Ba-VideoPip 是一款画中画方式的视频悬浮窗插件。支持点播、直播&#xff1b;支持官方、三方播放器无缝切换&#xff1b;支持动态刷新&#xff08;如切换视频或进度&#xff09;。 支持点播、直播支持官方、三方播放器无缝切换支持动态…

【unity3D】Collider碰撞器组件

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于游戏开发的学习笔记 &#x1f236;本篇是unity的Collider碰撞器组件 Collider碰撞器组件▶碰撞器类别▶Box Collider▶Sphere Collider▶Box Collider 2D▶Composite Collider 2…

Aspose.Words for .NET Crack 23.1.0

Aspose.Words for .NET Crack Aspose.Words 及其产品系列是一组 Word 文档&#xff0c;它们处理 API 以创建、编辑、打印、阅读和稍后转换所有格式化的 Word 文档和文件格式&#xff0c;就在 .NET、Java、Android 中、Cloud、SSRS、SharePoint 以及最后但并非最不重要的 Jasper…

【fpdlink显示】DS90UB948关于941连接948 GPIO调试问题

1. 前言 使用一个941连接2 948显示: 941的GPIO0/1/2/3到前948的GPIO0/1/2/3和941的D_GPIO0/1/2/3到948的GPIO0/1/2/3。 设置gpio3输出,则941的D_GPIO3设置高,而948的gpio3设置不高; 但设置gpio3输入后,看到941的D_GPIO3跟随948的gpio3变化。 2. 原理图连接 3. 测试代码…

通过图像了解 Git

我受到Nico Riedmann 的 Learn git concepts, not commands 的启发&#xff0c;我用我自己的方式总结了 git。当然&#xff0c;我也通过阅读官方文档来补充它。从系统结构上理解git&#xff0c;让git更有趣。我最近对 git 上瘾了&#xff0c;以至于我正在创建自己的 git 系统。…

Java集合常见面试题(一)

集合概述 Java 集合&#xff0c; 也叫作容器&#xff0c;主要是由两大接口派生而来&#xff1a;一个是 Collection接口&#xff0c;主要用于存放单一元素&#xff1b;另一个是 Map 接口&#xff0c;主要用于存放键值对。对于Collection 接口&#xff0c;下面又有三个主要的子接…

HarmonyOS智能座舱体验是怎样炼成的?立即查看

目录 一、智能座舱的人因设计理念 1.驾驶场景“2s内安全交互” 2.屏幕信息科学布局 3.屏幕1:2比例特色分屏 二、如何对座舱应用高效设计开发 1.基础要求 2.开发禁止 3.更佳体验 1.音频类应用快速接入 2.手机服务卡片快速适配 三、软硬件联合打造优秀体验 1.小憩模式 2.K歌体验 …

RepVGG:让VGG风格的ConvNets再次伟大

论文地址&#xff1a;https://arxiv.org/abs/2101.03697 我们提出了一种简单但功能强大的卷积神经网络结构&#xff0c;该模型在推理时类似于VGG&#xff0c;只有33的卷积和ReLU堆叠而成&#xff0c;而训练时间模型具有多分支拓扑结构。训练时间和推理时间结构的这种解耦是通过…