假设需要定义一个结构,这个结构包括一个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;
对于弹性数组成员,需要注意两点:
- 在计算结构体大小时(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;
}
- 数组一定是结构体的最后一个成员。否则会编译报错。
参考文献
- https://blog.csdn.net/gatieme/article/details/64131322
- <<C语言程序设计现代方法>> 第2版修订版
|