浅议柔性数组
很多时候,柔性数组应用在了变长结构体中,如:
StructPacket { Int state; Int len; Char cData[0]; //这里的0长结构体就为变长结构体提供了非常好的支持 };
首先对0长数组做一个解释:
用途 :长度为0的数组的主要用途是为了满足需要变长度的结构体。
用法 :在一个结构体的最后 ,申明一个长度为0的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量, 数组名这个符号本身代 表了一个不可修改的地址常量 (注意:数组名永远都不会是指针! ),但对于这个数组的大小,我们可以进行动态分配
对于0长数组的这个特点,很容易构造出变成结构体,如缓冲区,数据包等等:
Struct Buffer{ int proto; int cmd; int ver; char buff[]; };
我们使用sizeof求一下这个结构体的大小发现咦?为什么会是12而不是16或者13或者。。。其实又回到之前的问题解释上了,这里的0其实主要是为了满足变长结构体的需要,在求其所占内存大小的时候是不参与计算的。
这样的变长数组常用于网络通信中构造不定长数据包,不会浪费空间浪费网络流量,比如我要发送1024字节的数据,如果用定长包,假设定长包的长度为2048,就会浪费1024个字节的空间,也会造成不必要的流量浪费
Struct packet { int proto; int cmd; int ver; char buff[1024]; } packet p; memcpy(p.data,"1024 datas.........",1024) send(socket,(char*)&p,sizeof(p));
由于考虑到数据的溢出,变长数据包中的data数组长度一般会设置得足够长足以容纳最大的数据,因此packet中的data数组很多情况下都没有填满数据,因此造成了浪费,而如果我们用变长数组来进行封包的话,就不会造成浪费(最多会造成4个字节的浪费,包头的int型的len不属于数据因此算是浪费),如前面的Buffer结构体,假如我们要发送1024个字节,我们如何构造这个数据包呢:
char *tmp = (char*)malloc(sizeof(Buffer)+1024)
//这句代码的作用是申请一块连续的内存空间,这块内存空间的长度是Buffer的大小加上1024数据的大小,由两部分构成,sizeof(Buffer)和1024,如果仔细观察的话,会发现这种申请方法比第一种多了一段sizeof(Buffer)大小的空间,原因何在?如下
Buffer *p = (Buffer*)tmp; p->len = 1024; memcpy(p.cData,"1024 datas............",1024);
如上三行代码,首先做一个强制类型转换,Buffer类型的指针指向内存的起始位置,这段内存要分两部分使用,前部分4个字节p->len,作为包头(就是多出来的那部分),这个包头是用来描述紧接着包头后面的数据部分的长度,这里是1024,所以前四个字节赋值为1024(既然我们要构造不定长数据包,那么这个包到底有多长呢,因此,我们就必须通过一个变量来表明这个数据包的长度,这就是len的作用),而紧接其后的内存是真正的数据部分,通过p->cData定位到该部分的起始地址,最后,进行一个memcpy()内存拷贝,把要发送的数据填入到这段内存当中,最后:
send(socket,p,sizeof(Buffer)+1024);发送数据
给出一个程序看看:
#include <iostream> #include <malloc.h> using namespace std; typedef struct s_test { int i; double b; char ch[]; // or char ch[]; }st,*pst; int main() { char ch1[] = "my name is gaoke,Diao Si!!"; char ch2[] = "programer !"; pst pstest1 = (s_test*)malloc( sizeof(s_test) + strlen(ch1) + 1 ); if ( NULL != pstest1 ) { pstest1->i = 1; pstest1->b = 11; strcpy(pstest1->ch,ch1); } cout << "pstest1: " << pstest1->i << " " << pstest1->b << " " << pstest1->ch << endl; pst pstest2 = (pst)malloc( sizeof(st) +strlen(ch2) + 1 ); if ( NULL != pstest2 ) { pstest2->i = 2; pstest2->b = 22; strcpy(pstest2->ch,ch2); } cout << "pstest2: " << pstest2->i << " " << pstest2->b << " " << pstest2->ch << endl; free( pstest1 ); free( pstest2 ); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-03-05 使用Mutex实现Windows下进程间互斥访问同一资源