结构体成员最后一个定义0长度数组

问题:最近看IO 块设备的访问方法,其中一bio 结构体最后一个是长度为0 的数组,这个有什么意义呢?
 
struct bio {
    sector_t        bi_sector;    /* 磁盘上相关扇区 */
    struct bio        *bi_next;    /* 请求列表 */
    struct block_device    *bi_bdev; /* 相关的块设备 */
    unsigned long        bi_flags;    /* 状态和命令标志 */
    unsigned long        bi_rw;        /* 读还是写 */

    unsigned short        bi_vcnt;    /* bio_vecs的数目 */
    unsigned short        bi_idx;        /* bio_io_vect的当前索引 */

    /* Number of segments in this BIO after
     * physical address coalescing is performed.
     * 结合后的片段数目
     */
    unsigned int        bi_phys_segments;

    unsigned int        bi_size;    /* 剩余 I/O 计数 */

    /*
     * To keep track of the max segment size, we account for the
     * sizes of the first and last mergeable segments in this bio.
     * 第一个和最后一个可合并的段的大小
     */
    unsigned int        bi_seg_front_size;
    unsigned int        bi_seg_back_size;

    unsigned int        bi_max_vecs;    /* bio_vecs数目上限 */
    unsigned int        bi_comp_cpu;    /* 结束CPU */

    atomic_t        bi_cnt;        /* 使用计数 */
    struct bio_vec        *bi_io_vec;    /* bio_vec 链表 */
    bio_end_io_t        *bi_end_io; /* I/O 完成方法 */
    void            *bi_private;    /* bio结构体创建者的私有方法 */
#if defined(CONFIG_BLK_DEV_INTEGRITY)
    struct bio_integrity_payload *bi_integrity;  /* data integrity */
#endif
    bio_destructor_t    *bi_destructor;    /* bio撤销方法 */
    /*
     * We can inline a number of vecs at the end of the bio, to avoid
     * double allocations for a small number of bio_vecs. This member
     * MUST obviously be kept at the very end of the bio.
     * 内嵌在结构体末尾的 bio 向量,主要为了防止出现二次申请少量的 bio_vecs
     */
    struct bio_vec        bi_inline_vecs[0]
}

如果申请做下面的处理:
struct bio *buf;
int bio_vec_len    /*缓冲区数据的长度*/

/*直接把buffer的结构体跟存放数据的内存一起分配了*/
buf = (struct bio *) malloc(sizeof(struct bio) + bio_ven_len*sizeof(struct bio_vec));
buf->bio_vec = "数据struct bio_vec data\n";

/*也可以下面这样引用:*/
buf->bio_vec = “初始化数据”;  /*bio_vec data的取值范围:0~bio_vec_len-1*/

在结构体中,我们定义了0长度的数组,按理buf->bio_vec =“初始化数据”;属于越界访问,但是我们把结构体后面的n*sizeof(struct bio_vec)个长度的空间也一起申请了,所以该访问是合法的!

结构体最后使用0或1的长度数组的原因,主要是为了方便的管理内存缓冲区,如果你直接使用指针而不使用数组,那么,你在分配内存缓冲区时,就必须分配结构体一次,然后再分配结构体内的指针一次,(而此时分配的内存已经与结构体的内存不连续了,所以要分别管理即申请和释放)而如果使用数组,那么只需要一次就可以全部分配出来,(见下面的例子),反过来,释放时也是一样,使用数组,一次释放,使用指针,得先释放结构体内的指针,再释放结构体。还不能颠倒次序。

其实就是分配一段连续的的内存,减少内存的碎片化。

example:
在Linux系统里,/usr/include/linux/if_pppox.h里面有这样一个结构:
struct pppoe_tag {
    __u16 tag_type;
    __u16 tag_len;
    char tag_data[0];
} __attribute ((packed));
最后一个成员为可变长的数组,对于TLV(Type-Length-Value)形式的结构,或者其他需要变长度的结构体,用这种方式定义最好。使用起来非常方便,创建时,malloc一段结构体大小加上可变长数据长度的空间给它,可变长部分可按数组的方式访问,释放时,直接把整个结构体free掉就可以了。例子如下:
struct pppoe_tag *sample_tag;
__u16 sample_tag_len = 10;
sample_tag = (struct pppoe_tag *)malloc(sizeof(struct pppoe_tag)+sizeof(char)*sample_tag_len);
sample_tag->tag_type = 0xffff;
sample_tag->tag_len = sample_tag_len;
sample_tag->tag_data[0]=....
...
释放时,
free(sample_tag)

更多详细网址:
http://stackoverflow.com/questions/11733981/what-is-the-purpose-of-a-zero-length-array-in-a-struct
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html

你可能感兴趣的:(编程技巧)