首先说明一下,C/C++中不存在真正意义上的变长结构体。因为结构体在编译期就已经决定了其长度。如果需要实现变长,只能通过运行期分配内存来实现。

1,使用指针实现变长结构体

类似下面的代码应该非常常见:

typedef struct st_type4
{
	int		cnt;
	char	*data;
} type_d;

我们在结构体中定义了一个data字段,且该字段是一个char型的指针。因为是指针,所以我们可以在程序中动态分配其大小,从而实现变长结构体。具体使用的例子见文章后面。

2、使用零长度数组实现变长结构体

比如下面的代码:

typedef struct st_type1
{
	int 	cnt;
	char	item[0];
} type_a;

typedef struct st_type2
{
	int 	cnt;
	char	item[];
} type_b;

typedef struct st_type3
{
	int 	cnt;
	char	item[1];
} type_c;

这里一共列举了三种形式,主要是因为标准C或者有些编译器里面不允许零长度的数组,这个时候就可以使用第二种或者第三种形式来定义变长数组了。看一个完整的例子。

3、变长数组例子

#include <stdio.h>
#include <stdlib.h>

typedef struct st_type1
{
	int 	cnt;
	char	item[0];
} type_a;

typedef struct st_type2
{
	int 	cnt;
	char	item[];
} type_b;

typedef struct st_type3
{
	int 	cnt;
	char	item[1];
} type_c;

typedef struct st_type4
{
	int	cnt;
	char	*data;
} type_d;


int main()
{

	printf("len: %ldn", sizeof(type_a));
	printf("len: %ldn", sizeof(type_b));
	printf("len: %ldn", sizeof(type_c));

	type_a	a;
	type_a  *p_a = (type_a *)malloc(sizeof(type_a) + 100 * sizeof(char));
	strcpy(p_a->item, "type_a");
	printf("p_a->item:%sn", p_a->item);

	free(p_a);

	type_d	d;
	d.data = malloc(100 * sizeof(char));
	strcpy(d.data, "type_d");
	printf("d.data:%sn", d.data);

	free(d.data);

	return 0;
}

程序运行结果:

allan@ubuntu:temp$ ./a.out 
len: 4
len: 4
len: 8
p_a->item:type_a
d.data:type_d

可见使用指针和使用数组实现的变长数组功能是一样的。

4、分析

既然使用指针和使用数组都可以实现变长数组,那两者各有什么优劣呢?

利用指针实现的变长数组:该方法是最传统和通用方法,也是兼容性和移植性最好的一种写法。特别是在嵌入式领域或者一些不支持C99的应用场合,这这种实现方法无疑是最保险的。其不好的地方就在于内存的管理稍微比较麻烦:一般需要先给结构体分配内存,然后再给结构体里面的指针分配内存;释放时,必须先释放指针指向的内存,再释放结构体占用的内存(如果结构体是使用类malloc之类的函数分配的内存的话)。而且这样结构体占的内存一般与指针指向的内存是不连续的。

利用数组实现的变长数组:该方法比较巧妙,其实是利用了C/C++并不检查数组“越界”这一个特点。这样分配的内存一般是连续性的,可以减小内存碎片,而且分配和释放都只需要依次,管理相对容易。