首页 > 其他分享 >关于零长度数组的思考

关于零长度数组的思考

时间:2024-04-29 18:00:09浏览次数:21  
标签:body 数组 buffer packet len Packet 思考 长度 sizeof

首先看一下以下的结构声明

struct Packet
{
    int cmd;
    int len;
    char body[0];
};

可以看到body被声明为一个长度为0的字符数组。经过测试,sizeof(Packet)的值为8,也就是说body实际上并没有分配内存。这种数组被称作零长度数组(Arrays of Length Zero)或者柔性数组,其中char body[0];也可以写为char body[];

那引入零长度数组目的是什么呢?

通常在网络通信的过程总,缓冲区收发数据可能使用定长缓冲区,比如下面的声明:

struct Packet
{
    int cmd;
    int len;
    char body[1024];
};

如此一来数据包长度固定为8+1024,这么做会对程序提出要求,即为了防止溢出,数据不能超过1024个字节。另外一个缺点在于,如果是收发body较小的比如只有10个字节的数据包时,实际上就会有1014个无意义的数据参与了传输,这就造成了资源的浪费。

有没有一种理想的情况,每一次参与网络传输的数据包的大小都恰到好处,从而不浪费额外的空间?

我们想到了使用指针来动态申请内存,比如这样:

struct Packet
{
    int cmd;
    int len;
    char * body;
};

在使用时对body分配内存

Packet packet;
packet.cmd = Login;
packet.len = sizeof(buffer);
packet.body = new char[packet.len];
memcpy(packet.body,buffer,packet.len);

如此一来似乎确实能够满足数据包不定长数据包的需求,但是这种方式下,pakcet的内存和packet.body内存不能保证是连续的,而且需要手动管理packet.body的内存。

为了解决这些痛点,C99标准引入了一个新的特性:柔性数组。

定义如下:

struct test {
    int len;  	// 必须至少有一个其它成员
    char data[]; 	// 柔性数组必须是结构体最后一个成员(也可是其它类型)
};//size = 4

通过一个例子来看看柔性数组如何读写数据。

#include <cstdio>
#include <cstdlib>
#include <cerrno>
#include <cstring>

//定义一个柔性数组
struct Packet
{
    int len;			//必须至少有一个其它成员
    char body[0];	//柔性数组必须是结构体最后一个成员
};

int main()
{
    printf("sizeof Packet = %lu\n", sizeof(Packet));

    char buffer[]="hello world!";

    //分配一整块内存
    auto * packet = (Packet*) malloc(sizeof(Packet) + sizeof(buffer));
    if (packet == NULL)
    {
        printf("malloc error:%s\n", strerror(errno));
        return -1;
    }

    //写入数据到柔性数组
    packet->len = sizeof (buffer);
    //buffer的数据复制到packet->body
    memcpy(packet->body,buffer,sizeof (buffer));

    printf("sizeof(*packet) = %lu\n", sizeof(*packet));
    printf("sizeof(packet) = %lu\n", sizeof(packet));
    printf("sizeof(packet->body) = %lu\n", sizeof(packet->body));

    //读取数据
    char buffer2[packet->len];
    memcpy(buffer2,packet->body,packet->len);
    printf("packet.body = %s\n", packet->body);
    printf("buffer2 = %s\n", buffer2);

    //销毁柔性数组
    free(packet);
    packet = NULL;
    return 0;
}

运行这份代码,结果如下

sizeof Packet = 4
sizeof(*packet) = 4
sizeof(packet) = 8
sizeof(packet->body) = 0
packet.body = hello world!
buffer2 = hello world!

到此似乎已经能够了解这个零长度数组或者说柔性数组是个什么东西了,接下来看一下内存布局印证一下心中猜想。

可以看到,0x0000600002960000为packet的地址,0x0000600002960000开始的四个字节存储的是0d000000,即十进制13的小端表示,这也就是buffer数据的长度,其后紧跟body数组的内存。

类似于定义以下结构体,但在分配内存的时候多分配一些用来存放buffer,多分配的长度根据buffer的长度确定。巧妙之处在于body不占据结构体的内存,相当于仅仅是一个偏移量,可以轻松的根据body找到多分配的那一串内存。


struct Packet
{
    int len;
};

通过以上的实验,突然有了一个新的想法,直接去掉char body[0];这行,然后直接在该结构体的尾部分配一块内存存放buffer内容:


#include <cstdio>
#include <cstdlib>
#include <cstring>

//定义一个柔性数组
struct Packet
{
    int len;
		//    char body[0];
};

int main()
{
    printf("sizeof Packet = %lu\n", sizeof(Packet));

    char buffer[]="hello world!";

    //分配一整块内存
    auto * packet = (Packet*) malloc(sizeof(Packet) + sizeof(buffer));

    //写入数据到柔性数组
    packet->len = sizeof (buffer);
    memcpy(packet+(int)sizeof(*packet),buffer,sizeof (buffer));

    //读取数据
    char buffer2[packet->len];
    memcpy(buffer2,packet + (int) sizeof(*packet),packet->len);
    printf("packet.body = %s\n", (char * )(packet+(int)sizeof(*packet)));
    printf("buffer2 = %s\n", buffer2);

    //销毁柔性数组
    free(packet);
    packet = NULL;
    return 0;
}

标签:body,数组,buffer,packet,len,Packet,思考,长度,sizeof
From: https://www.cnblogs.com/pengpengda/p/18166405

相关文章

  • Godot的游戏开发思考(无代码)
    目录前言GDScriptorC#?C#IOC开发代码和引擎的平衡Godot如何学习多而精炼的小Demo后面的学习的方向Ai绘画和Ai声音的学习前言我断断续续学了快半年的Godot了吧,从去年的Unity事件发生之后开始接触,然后断断续续学到了现在,这里就简单讲讲我对Godot的看法GDScriptorC#?GDScript......
  • Redis中对数组的获取类型转换
    1#####Redis中对数组的获取类型转换23```java4//判断redis中键值key是否存在;5BooleancarWeizi_redis_service=redisService.hasKey("carWeizi_redis_service");6if(carWeizi_redis_service){7//获取对应的list数组传入时re......
  • 【C语言】---- 数组
    在计算机编程中,数组是一种非常重要的数据结构,它可以用来存储多个相同类型的数据。在本文中,我们将深入探讨一维数组和二维数组,它们的定义、特性以及在编程中的应用。一维数组一维数组是最简单的数组形式之一,它是一组按顺序排列的元素的集合,每个元素都有一个唯一的索引。在C语言中......
  • vector开二维数组&&深搜迷宫问题&&BFS
    vector<vector>vis(N+10(一维的大小),vector(N+10(二维的大小),0(初始化赋值)),step(N+10,vector(N+10,0));vector<vector>vis(N+10,vector(N+10)),step(N+10,vector(N+10));开数组大小一定要超过题目本身大小;#include<bits/stdc++.h>usingnamespacestd;#defineintl......
  • 力扣-1979. 找出数组的最大公约数
    1.题目介绍题目地址(c-力扣(LeetCode))https://leetcode.cn/problems/find-greatest-common-divisor-of-array/题目描述给你一个整数数组nums,返回数组中最大数和最小数的最大公约数。两个数的 最大公约数是能够被两个数整除的最大正整数。 示例1:输入:nums=[2,5,6......
  • js数组去重
    functionisPrimitive(obj){returnobj===null||!['object','function'].includes(typeofobj)}functionobjEqual(obj1,obj2){//console.log(isPrimitive(obj1)||isPrimitive(obj2))if(isPrimitive(obj1)||isPrimitive(obj2)){......
  • 最强国产 Sora 大模型 Vidu 发布,长度可达 16 秒;微信文件 3 小时内可撤回丨 RTE 开发者
      开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(RealTimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑的个人观点,......
  • 一道神奇的面试题---无序数组排序后的最大相邻差
    一:概述这个算法的面试题目是:有一个无序整型数组,如何求出该数组排序后的任意两个相邻元素的最大差值?要求时间和空间复杂度尽可能低。  二:具体说明<1>第一种解法(初步解法)这个解法的大致思路:使用任意一种时间复杂度为O(nlogn)的排序算法(如快速......
  • 关于现代ITSM的一些思考
    现代IT服务管理(ITSM)的发展已经远远超越了传统的技术支持和故障修复范畴,它正逐步成为企业数字化转型和优化业务流程的关键驱动力。以下是对现代ITSM的一些深入思考:IT与业务融合加深:随着企业对信息技术依赖性的增强,ITSM不再仅仅是IT部门内部的事务,而是与业务战略紧密相连。现代ITS......
  • 一道编程题引发的C中关于数组、指针的思考
    7-163谷歌的招聘由一道编程题引发的C中关于数组、指针的思考先来看三种数组定义方式#include<stdio.h>#include<stdlib.h>intmain(){//方式1intarray_1[4]={1};//方式2,变长数组intn2;scanf("%d",&n2);intarray_2[n2];//使用变......