变长结构体 - CSDN
精华内容
参与话题
  • c语言变长结构体

    千次阅读 2017-08-27 15:48:34
    1.什么是变长数组 struct MyData { int nLen; char data[0]; }; sizeof(MyData)=4; 可能有的编译器不支持char data[0];需要用char data[1];代替,这样上面结构体大小是sizeof(MyData)=8(字节对齐); 在上...

         1.什么是变长数组

    struct MyData 
    {
        int nLen;
        char data[0];
    };   
    	sizeof(MyData)=4;

    可能有的编译器不支持char data[0];需要用char data[1];代替,这样上面结构体大小是sizeof(MyData)=8(字节对齐);

    在上结构中,data是一个数组名;但该数组没有元素;该数组的真实地址紧随结构体MyData之后,而这个地址就是结构体后面数据的地址(如果给这个结构体分配的内容大于这个结构体实际大小,后面多余的部分就是这个data的内容),这种声明方法可以巧妙的实现C语言里的数组扩展

    如下下所示:

    #include <iostream>
    using namespace std;
    struct MyData 
    {
        int nLen;
        char data[0];			//如果这里用char* data;代替呢?是一个指针占用空间,而采用变长数组不占内存,数组名只是一个符号
    							//代表一个不可修改的地址常量
    };
    
    int main()
    {
        int nLen = 10;
        char str[10] = "123456789";
    
        cout << "Size of MyData: " << sizeof(MyData) << endl;
    
        MyData *myData = (MyData*)malloc(sizeof(MyData) + 10);
        memcpy(myData->data,  str, 10);
    
        cout << "myData's Data is: " << myData->data << endl;
    
        free(myData);
    
        return 0;
    }
    
    
    //输出:
    Size of MyData: 4
    myData's Data is: 123456789   
    //由于数组没有元素,该数组在该结构体中不分配占用空间,所以sizeof(struct Mydata) = 4。 

    实际用时采取这样:
             struct MyData *p = (struct MyData *)malloc(sizeof(struct MyData )+strlen(str))

    这样就可以通过p->data 来操作这个str。


    struct MyData1 
    {
        int nLen;
        char data[0]; //char data[1];有的编译要求这样写

    };

    struct MyData2 
    {
        int nLen;
        char*data;

    };

    对于上面两个结构体有下面几点说明:
    1. MyData1  (char data[0])结构体占用内存最小,Mydata2有个指针占用4B
    2.MyData1与前面结构体数据是连续的内存存储空间,而MyData2下,新增加数据data是单独开辟的空间;
    3.释放内存时,MyData1可以直接释放,而MyData2需要先释放指针指向内存,然后再释放结构体数据部分否则会内存泄漏



    展开全文
  • 深入浅出变长结构体

    2020-09-10 15:50:33
    深入浅出变长结构体 1、 问题的引出 项目中用到数据包的处理,但包的大小是不固定的,其长度由包头的2字节决定。比如如下的包头:88 0f 0a ob cd ef 23 00 。长度由头2个字节880f决定,考虑字节序,转为0f88,转为10...

    原文链接

    深入浅出变长结构体

    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、 什么叫变长结构体?

     如下所示:
    
    struct Var_Len_Struct
    {
         int nsize;
         char buffer[0];
    };
    

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

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

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

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

    可能有的同学会问到,1引出部分如果说定义定长数组浪费空间,定义一个指针不也能指向变长的数据域部分吗?
    是的,是可以实现的。那么我们就对比下有什么不同。

    1. 结构体1:s_one,用指针指向数据域部分;
    2. 结构体2:s_two, 用[0]的数组;
    3. 结构体3:s_three, 因为有的编译器不支持[0],我们用[1]来表示;多了些存储。
    #include <stdafx.h>
    #include <iostream>
    using namespace std;
     
    const int BUF_SIZE = 100;
     
    struct s_one
    {
    ints_one_cnt;
    char*s_one_buf;
    };
     
    struct s_two
    {
    ints_two_cnt;
    chars_two_buf[0];
    };
     
    struct s_three
    {
    ints_three_cnt;
    chars_three_buf[1];
    };
     
    int main()
    {
    //赋值用
    constchar* tmp_buf = "abcdefghijklmnopqrstuvwxyz";
    intntmp_buf_size = strlen(tmp_buf);
     
    //<1>注意s_one 与s_two的大小的不同
    cout<< "sizeof(s_one) = " << sizeof(s_one) << endl; //8
    cout<< "sizeof(s_two) = " << sizeof(s_two) << endl; //4
    cout<< "sizeof(s_three) = " << sizeof(s_three) << endl;//5-->8结构体对齐
    cout<< endl;
     
    //为buf分配100个字节大小的空间
    intntotal_stwo_len = sizeof(s_two) + (1 + ntmp_buf_size) * sizeof(char);
    intntotal_sthree_len = sizeof(s_three) + ntmp_buf_size * sizeof(char);
     
    //给s_one buf赋值
    s_one*p_sone = (s_one*)malloc(sizeof(s_one));
    memset(p_sone,0, sizeof(s_one));
    p_sone->s_one_buf= (char*)malloc(1 + ntmp_buf_size);
    memset(p_sone->s_one_buf,0, 1 + ntmp_buf_size);
    memcpy(p_sone->s_one_buf,tmp_buf, ntmp_buf_size);
     
    //给s_two buf赋值
    s_two*p_stwo = (s_two*)malloc(ntotal_stwo_len);
    memset(p_stwo,0, ntotal_stwo_len);
    memcpy((char*)(p_stwo->s_two_buf),tmp_buf, ntmp_buf_size);  //不用加偏移量,直接拷贝!
     
    //给s_three_buf赋值
    s_three*p_sthree = (s_three*)malloc(ntotal_sthree_len);
    memset(p_sthree,0, ntotal_sthree_len);
    memcpy((char*)(p_sthree->s_three_buf),tmp_buf, ntmp_buf_size);
     
    cout<< "p_sone->s_one_buf = " << p_sone->s_one_buf<< endl;
    cout<< "p_stwo->s_two_buf = " << p_stwo->s_two_buf<< endl;
    cout<< "p_sthree->s_three_buf = " <<p_sthree->s_three_buf << endl; //不用加偏移量,直接拷贝!
    cout<< endl;
     
    //<2>注意s_one 与s_two释放的不同!
    if(NULL != p_sone->s_one_buf)
    {
            free(p_sone->s_one_buf);
            p_sone->s_one_buf= NULL;
     
            if(NULL != p_sone)
            {
                   free(p_sone);
                   p_sone= NULL;
            }
            cout<< "free(p_sone) successed!" << endl;
    }
     
    if(NULL != p_stwo)
    {
            free(p_stwo);
            p_stwo= NULL;
     
            cout<< "free(p_stwo) successed!" << endl;
    }
     
    if(NULL != p_sthree)
    {
            free(p_sthree);
            p_sthree= NULL;
     
            cout<< "free(p_sthree) successed!" << endl;
    }
     
    return0;
    }
    

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

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

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

    4、变长结构体的应用

    <1>Socket通信数据包的传输;
    <2>解析数据包,如笔者遇到的问题。
    <3>其他可以节省空间,连续存储的地方等。

    展开全文
  • C++之变长数组与变长结构体

    千次阅读 2018-03-18 19:54:52
    在C99标准中,新加入了变长数组和变长结构体变长数组如下:其中a[n]就是变长数组,b[10]就是定长数组int main() { int b[10]; int n = 10; int a[n]; }该变长数组也可以对应于malloc的动态空间分配,等价于int *...

    在C99标准中,新加入了变长数组和变长结构体

    变长数组如下:其中a[n]就是变长数组,b[10]就是定长数组
    int main() {
        int b[10];
        int n = 10;
        int a[n]; 
    }

    该变长数组也可以对应于malloc的动态空间分配,等价于

    int *a = malloc(n * sizeof(int));


    二者的区别:
    1.变长数组从栈上分配空间,malloc从堆上分配空间;

    2.栈上分配和回收空间比堆上分配和回收空间要快;
    3.当n过大时,可能导致栈溢出,但malloc分配时不会。

    变长结构体:
    变长结构体是将数组放在结构体末尾,通过给变长结构体动态分配内存时,额外分配更多的空间做buffer。值得注意的是,数组名是一个偏移量,本身不会占用空间,数组名代表的是一个不可修改的地址常量,但是我们对这个数组名指向的空间可以随意的指定。
    变长结构体分配额外的空间做buffer的例子如下:
    #include <stdio.h>
    #include <stdlib.h>
    struct v_struct { 
        int i; 
        int a[0];
    };
    
    int main() 
    { 
        v_struct *pv = (v_struct *)malloc(sizeof(v_struct)+sizeof(int)* 100); 
        pv->a[50] = 100; 
        printf("sizeof int is equal to %u\n", sizeof(int)); 
        printf("sizeof v_struct is equal to %u\n", sizeof(v_struct));
        printf("%u\n", sizeof(pv)); 
        return 0;
    }
    运行结果为,两者的大小均为4

    顺带打印了指针的大小,若机器是32寻址的,则指针大小为4字节,若机器是64位寻址的,则指针大小为8字节

    变长结构体的作用:

    1.变长结构体的变长部分通常可以用来作为buffer,缓存数据。

    2.变长结构体的空间释放是很方便的,只需要free(pv), 而且不会造成内存泄露。

    3.变长结构体使用起来更方便,不过,要小心越界。


    为何不用指针代替变长结构体?
    指针与变长结构体的区别:
    1.位置方面:指针可以放在任何地方,但是变长结构体的变长部分一定要放在结构体的尾部;

    2.内存占用方面:指针会占用一个指针大小的内存空间,但是变长数组不占内存,它只是一个占位符;
    3.内存布局方面:指针指向的内存和结构体的内存可以是不连续的,但是变长部分和结构体的内存必须是连续的。
    4.内存释放方面:使用指针,需要先释放指针所指向的内存,再释放整个结构体的内存,否则会造成内存泄漏;而使用变长结构体可以直接释放整个结构体的空间;
    5.限制方面:指针可以用在C++的类中,而变长结构体不可以。因为有些编译器会将一些额外的信息放在类的最后,比如vptr或者虚基类的内容,使用了变长的类,就会把这部分的值改变,这种行为是未定义的。

    参考博客:
    http://blog.csdn.net/SKY453589103/article/details/51147505
    展开全文
  • 解析变长结构体的用法和优点

    千次阅读 2017-05-14 10:08:06
    变长结构体:在接触变长结构体之前,以为会是一个很难理解的东西,但是这其实算是C里面的一种技巧吧,优点是:分配了一段连续的内存,防止内存碎片化以及方便内存的管理。使用变长结构体的格式如下:struct Test { .......

    变长结构体:


    在接触变长结构体之前,以为会是一个很难理解的东西,但是这其实算是C里面的一种技巧吧,优点是:分配了一段连续的内存,防止内存碎片化以及方便内存的管理

    使用变长结构体的格式如下:

    struct Test
    {
      ....
      int a;
      ....
      char b[0];
    };
    

    重点是结构体的最后一个成员char b[0],是个空数组在我们不知道结构体内的某个成员大小是多少的时候,我们在最后一个成员放置了一个空数组,这样做的好处就是,我们直接用结构体指针申请空间(sizeof(struct) + 给空数组申请的空间),就完成了动态分配

    这里可能读者会有一个疑惑,那就是指针也同样完成这个任务,为什么不用指针呢。这里我们就用一个例子来说明:

    #include <iostream>
    using namespace std;
    #include <cstring>
    #include <stdlib.h>
    typedef struct
    {
        int a;
        char b[0];
    }Empty;
    typedef struct
    {
        int x;
        char *y;
    }Ptr;
    int main()
    {
        //变长结构体申请内存
        Empty *p_array = (Empty *)malloc(sizeof(Empty) + 100);  //可以看到这里直接申请了变长结构体大小的空间再加100字节,这100字节就相当于是给char b[0]用的了
        //常规方式申请内存,先申请结构体的,再申请指针的
        Ptr *p_ptr = (Ptr *)malloc(sizeof(Ptr));
        p_ptr->y = (char *)malloc(100);
        //初始化malloc申请的内存
        memset(p_array, 0, sizeof(p_array));
        memset(p_ptr, 0, sizeof(p_ptr));
        memset(p_ptr->y, 0, 100);
    
    
        //拷贝字符串并输出
        strcpy(p_array->b, "test");
        strcpy(p_ptr->y, "test");
        cout << p_array->b << endl;
        cout << p_ptr->y << endl;
    
        //释放空间
        cout << "--------------------------" << endl;
        if(p_array != NULL)
        {
            cout << "1.释放变长结构体的空间" << endl;
            free(p_array);
            p_array = NULL;
        }
        cout << "--------------------------" << endl;
        if(p_ptr->y != NULL)
        {
            cout << "1.释放指针申请的空间" << endl;
            free(p_ptr->y);
            p_ptr->y = NULL;
            if(p_ptr != NULL)
            {
                cout << "2.释放结构体的空间" << endl;
                free(p_ptr);
                p_ptr = NULL;
            }
        }
        return 0;
    }
    

    两者相比较,得出以下主要结论:
    变长结构体的内存是连续的(严谨的说是虚拟内存),而常规方法的不是,所以变长结构体只需释放一次空间,而常规方法需要释放两次

    并且我们使用sizeof查看两个结构体的大小会发现,Empty结构体的大小为4字节,而Ptr的大小为8字节,可以得出char b[0]是不占内存空间的。

    最后还有需要注意的一点就是,有些编译器可能不支持char b[0]这样的写法,可以换成char b[1],用法还是一样的,只是这时的结构体大小变成了4 + 1 + 3 = 8字节,最后那个3字节是由于内存对齐填补上的。

    展开全文
  • 零长度和变量长度数组GNU C 允许使用零长度数组,在定义变长对象的头结构时,这个特性在一些数据进行网络通信时非常有用。 但是零长度的数组编译环境不同会出问题,所以用下面的格式。 typedef struct package{ int...
  • C++变长结构体

    2019-03-03 08:33:06
    结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存...
  • C语言/ 变长结构体

    2018-01-29 14:54:00
    深入浅出变长结构体 引自:http://blog.csdn.net/pi9nc/article/details/11924295 深入浅出变长结构体 1、 问题的引出 项目中用到数据包的处理,但包的大小是不固定的,其长度由包头的2字节决定。比如...
  • 变长结构体

    2012-07-25 23:51:05
    看到老师写的一个结构体很好奇,结构体的最后是一个长度为0的数组,当时感觉老师是不是写错了,这样写意义何在呢?都没有分配空间,貌似没有存在的意义。 后来网上查了一下,其实这是在很多高级的东东里面都用到的...
  • C99_变长结构体实现

    千次阅读 2014-10-16 22:41:42
    /************************************************************************* > File Name: C99_lengthenStruct.c > Author: zshh0604 > Mail: zshh0604@163.com > Created Time: 2014年10月16日 星期
  • 变长结构体用法总结

    千次阅读 2017-05-21 21:03:36
    结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存...
  • 变长结构体

    2017-10-20 13:31:14
    在Linux系统里,/usr/include/linux/if_pppox.h里面有这样一个结构: struct pppoe_tag {  __u16 tag_type;  __u16 tag_len;  char tag_data[0];...最后一个成员为可变长的数组,对于TLV(Type-L
  • 变长结构体的实现

    2013-06-17 22:42:11
    #include #include #include struct aa{   int a;  int b; }; struct bb{   struct aa test[0]; }; int main(void) ... struct bb *p=(struct bb*)malloc(sizeof(struct bb)+sizeof(s
  • 变长结构体的几种方法

    千次阅读 2009-09-30 16:38:00
    定义结构体时,往往其成员中含有变长的元素,可用以下方法解决 一、定义变长数组struct CToolBarData{ WORD wVersion; //版本号 ==1 WORD wWidth; WORD wHeight; WORD wItemCount; //给出后面有几项 WORD ...
  • c语言系统网络中发送变长结构体数据包 最近在做关于c语言系统网络,记录下关于发送变长结构体数据包 发送一个自定义的结构体,将结构体的的数据部分声明成如下形式: struct Data { int data_len; int type; char...
  • 变长结构体的表示方法

    千次阅读 2010-08-07 17:31:00
    Linux程序中,经常看到变长结构体的定义。其中,相当一大部分是通过零长数组来实现的。
  • #include <iostream> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <unistd.h> #include <string.h> #define HexPrint(_buf, _len) \ ... int _m_le.
  • typedef struct tagTest { int uID; int iNum; ... //可变长数据,根据所需要数据类型定义 }Test; data是一个数据,个数为1,但是其实在使用的时候,不一定为1,也就是说在上述结构
1 2 3 4 5 ... 20
收藏数 47,991
精华内容 19,196
热门标签
关键字:

变长结构体