zoukankan      html  css  js  c++  java
  • 深入浅出C语言中的柔性数组

    问题引出

    在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如:

    typedef struct
    {
        WORD usMsgID;
        WORD usMsgLen;
        char *pData;//pData指向字符串
    }COMMMON_DEF_STRU

    针对这种普通形式的结构体,我们很容易写出如下代码:

    COMMMON_DEF_STRU* pCommonStru = (COMMMON_DEF_STRU*)malloc(sizeof(COMMMON_DEF_STRU));
    pCommonStru->usMsgID          = 0x1234;
    pCommonStru->usMsgLen         = sizeof(szInfo);
    //创建字符串存储buffer
    pCommonStru->pData            = (char*)malloc(pCommonStru->usMsgLen);
    
    //拷贝字符串到pDatabuffer
    memcpy_s(pCommonStru->pData,pCommonStru->usMsgLen,szInfo,pCommonStru->usMsgLen);
    
    //数据操作
    printf("print data: %s
    ",pCommonStru->pData);
    
    //释放字符串所指的内存
    SAFE_FREE(pCommonStru->pData);
    //释放整个结构体的内存,需要释放两次,而且有先后顺序关系
    SAFE_FREE(pCommonStru);

    从上面代码可以看出,使用设计我们会进行两次的内存分配和内存释放,释放过程需要注意前后顺序关系,整个过程相对麻烦一点;使用这种方法的结构体设计,容易造成数据(字符串)与结构体本身是分离的,对后续释放造成麻烦。如果把字符串跟结构体直接连在一起,不是更好吗?

    解决方案

    针对以上问题 可以有一个更优的解决方案即,在结构体的尾部放置一个0长度的数组是一个绝妙的解决方案,我们既能直接引用该字符串,又不占用结构体的空间。不过,C/C++标准规定不能定义长度为0的数组,因此,有些编译器就把0长度的数组成员作为自己的非标准扩展。

    在讲述柔性数组成员之前,首先要介绍一下不完整类型(incomplete type)。不完整类型是这样一种类型,它缺乏足够的信息例如长度去描述一个完整的对象,它的出现反映了C程序员对精炼代码的极致追求,这种代码结构产生于对动态结构体的需求。

    鉴于这种代码结构所产生的重要作用,C99甚至把它收入了标准中。C99使用不完整类型实现柔性数组成员;其定义如下:

    柔性数组:在结构体中的最后一个元素允许是未知大小的数组,这样的数组做柔性数组(flexible array)成员(也叫伸缩性数组成员)。

    包含柔性数组的结构体设计

     typedef struct
     {
        WORD usMsgID;
        WORD usMsgLen;
        char pData[0];//柔性数组
     }FLEXIBLE_DEF_STRU;

    有些编译器会报错无法编译可以改成:

     typedef struct
     {
        WORD usMsgID;
        WORD usMsgLen;
        char pData[];//大小可变
     }FLEXIBLE_DEF_STRU;

    针对这种包含柔性数组的结构体设计,有以下几点注意事项

    1. 结构体中的柔性数组成员前面必须至少一个其他成员。

    2. 柔性数组必须是结构体的最后一个成员。

    3. 柔性数组成员只作为一个符号地址存在,sizeof 返回的这种结构大小不包括柔性数组的内存。

    4. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。

    5. 柔性数组成员不仅可以用于字符数组,还可以是元素为其它类型的数组。

    通过如下表达式给结构体分配内存:

    char szDataInfo[] = "hello world";  
    //malloc内存大小为结构体大小+数据大小,包含‘’
    FLEXIBLE_DEF_STRU *pTest = (FLEXIBLE_DEF_STRU *)malloc(sizeof(FLEXIBLE_DEF_STRU)+sizeof(szDataInfo));

    pData就是一个柔性数组成员,如果把pTest 指向的动态分配内存看作一个整体,pData就是一个长度可以动态变化的结构体成员,指向数据区的首地址,数据区的大小在分配时进行动态申请,柔性一词来源于此。

    柔性数组的使用请看下面的例子:

    char szInfo[] = "hello world";  
    FLEXIBLE_DEF_STRU *pTest = (FLEXIBLE_DEF_STRU *)malloc(sizeof(FLEXIBLE_DEF_STRU)+sizeof(szInfo));
    pTest->usMsgID      = 0x1234;
    pTest->usMsgLen     = sizeof(szInfo);
    
    //若拷贝的内容是字符串,注意''也需要被拷贝走,
    //所以使用sizeof(),不能是strlen()
    memcpy_s(pTest->pData,pTest->usMsgLen,szInfo, sizeof(szInfo));
    printf("%s
    ",pTest->pData);
    
    //只需要释放一次内存
    SAFE_FREE(pTest);

    对于包含有柔性数组的结构体,我们对内存的申请和释放只需要一次。因为我们的数据区buffer紧随在结构体之后,数据和结构体在同一个内存块中,因此我们只需要释放一次,示意图如下:

            这里写图片描述

    我们应当尽量使用标准形式,在非C99的场合,可以使用指针方法。需要说明的是:C89不支持这种东西,C99把它作为一种特例加入了标准。但是,C99所支持的是incomplete type,而不是zero array,形同int a[0];这种形式是非法的,C99 支持的形式是形同int a[];只不过有些编译器把int a[0];作为非标准扩展来支持,而且在C99 发布之前已经有了这种非标准扩展了,C99 发布之后,有些编译器把两者合而为一了。

    参考资料:
    http://blog.csdn.net/ce123_zhouwei/article/details/8973073

  • 相关阅读:
    PHP运行及语句及逻辑
    数据库基础,表及SQL语句
    php后台修改人员表信息
    php后台增加删除修改跳转页面
    用PHP访问数据库
    php登录注册页面及加载
    php做登录注册页面及加载
    实现基于物理的渲染
    Tile-Based Deferred Rendering
    矩阵基础 2
  • 原文地址:https://www.cnblogs.com/jinxiang1224/p/8468206.html
Copyright © 2011-2022 走看看