C语言弹性数组成员

987 阅读2分钟

假设需要定义一个结构,这个结构包括一个len字段和一个data字段。分别表示这个数据的长度和对应的数据。我们一般有以下几个方法这样做。

引言

方法一:定长数组

第一种方法就是使用定长数组。即对数组设置一个足够大的缓冲区。如:

struct vstring1 {
    int len;
    char data[N];
};

其中N是一个表示字符串最大长度的宏。考虑到字节对齐,则这个结构体的大小 大于等于:

sizeof(int) + sizeof(char) * N;

这样做既限制了我们使用字符串的长度(最大为N), 也会浪费许多的内存(可能大多数字符串并不需要N个字符串)。

方法二: 使用指针

定义如下的数据结构:

struct vstring2 {
    int len;
    char *data;
};

这种在很多代码中也非常常见,这种需要内存申请两次。第一次是为结构体vstring2申请内存,第二次是为结构体成员data申请内存。相比于使用定长数组。使用指针浪费的内存就只有一个指针域的大小了。缺点就是有可能我们只释放了结构体指针,忘记释放里面的成员data的内存,导致内存泄漏。其次的话,这两次内存申请是不连续的,可能对性能有一定的影响。

方法三:struct hack

第三种做法是声明data数组的长度是1,然后动态地分配每一个字符串:

struct vstring3 {
    int len;
    char data[1];
};
...
struct vstring3 *str = malloc(sizeof(struct vstring3) + n -1);
str->len = n;

这里使用了一种"欺骗"的方法,分配比该结构声明时应具有的内存(这个例子中是n-1个字符)更多的内存,然后使用这些内存存储data数组额外的元素。这种方法称为"struct hack"。 因为struct hack技术非常有用,所以从C99开始提供了弹性数组成员 (flexible array member)来达到同样的目的。

弹性数组成员

弹性数组成员是指结构的最后一个成员是数组时,其长度可以省略:

struct vstring {
    int len;
    char data[];
};

data数组的长度在为vstring结构分配内存时确定:

struct vstring *str = malloc(sizeof(struct vstring) + n);
str->len = n;

对于弹性数组成员,需要注意两点:

  1. 在计算结构体大小时(sizeof(struct vstring)),数组成员并不占用结构体的大小。
#include <stdio.h>
struct vstring {
    int len;
    char data[];
};

int main() 
{
    printf("%u, %u\n", sizeof(int), sizeof(struct vstring)); // 4,4 
    return 0;
}
  1. 数组一定是结构体的最后一个成员。否则会编译报错。

参考文献

  1. blog.csdn.net/gatieme/art…
  2. <<C语言程序设计现代方法>> 第2版修订版