Bootstrap

c语言中的柔性数组

也许你从来没有听说过 柔性数组( flexible array 这个概念,但是它确实是存在的。
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
struct S
{
	int i;
	char ch[0];//char ch[]
};
int main()
{

	return 0;
}

上面一个结构体中最后一个未指定大小的数组就是柔性数组。上面的俩种方法都是可以的。

柔性数组的特点:
一 结构中的柔性数组成员前面必须至少一个其他成员。结构体成员中不能就会只有一名柔性数组成员,应该还包含其他的非柔性数组的成员。
struct s
{
	int arr[];
};

二、sizeof返回的这种结构大小不包括柔性数组的内存
所以,当你用sizeof来计算一个含有柔性数组成员的结构体大小时,计算出的结果不包括柔性数组成员在内。
比如:

struct S
{
	int i;
	char ch[0];//char ch[]
};
int main()
{
	printf("%zd\n", sizeof(struct S));//结果是4
	return 0;
}

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

struct S
{
	int i;
	char ch[0];//char ch[]
};
int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(char));
	if (ps == NULL)//判断
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(ps->ch) = 'Q';
	}
	for (i = 0; i < 10; i++)
	{
		printf("%c ", *(ps->ch));
	}
	free(ps);
	ps = NULL;
	return 0;
}

在图中开辟的前面的sizeof开辟的是int i的空间,后面一个是柔性数组的空间。然后我们可以开辟的10个空间打印10个字符Q。

模拟实现柔性数组的功能
其实,我们若不借用柔性数组也能实现以上功能:

typedef struct S
{
	int i;
	char* ch;
}S;
int main()
{
	S* ps = (S*)malloc(sizeof(S));
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	ps->ch = (char*)malloc(sizeof(char) * 10);
		if (ps->ch == NULL)
		{
			perror("malloc");
			return 1;
		}
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(ps->ch) = 'Q';
		}
		char* plc = (char*)realloc(ps->ch, 20 * sizeof(char));
		if (plc != NULL)
		{
			ps->ch = plc;
		}
		else
		{
			perror("realloc");
			return 1;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%c ", *(ps->ch));
		}
		free(ps->ch);
		ps->ch = NULL;
		free(ps);
		ps = NULL;
	return 0;
}

柔性数组其实也就是结构体中的一个数组,准确来说,是一个空间大小可以自由变换的数组,那么我们在结构体中定义一个指针,使指针指向的空间可以自由变换(即指针指向的是动态开辟的内存),也就达到了这个效果。

注意: 这里释放动态内存空间时,需要先释放ps->ch指向的动态内存空间,再释放ps指向的动态内存空间。 如果我们先释放的是ps指向的动态内存空间,那么ps->ch所指向的空间就再也找不到了。

柔性数组的优点:

上述 代码 1 代码 2 可以完成同样的功能,但是 方法 1 的实现有两个好处:
第一个好处是: 方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给
用户。用户调用 free 可以释放结构体,但是用户并不知道这个结构体内的成员也需要 free ,所以你
不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好
了,并返回给用户一个结构体指针,用户做一次 free 就可以把所有的内存也给释放掉。
第二个好处是: 这样有利于访问速度 .
连续的内存有益于提高访问速度,也有益于减少内存碎片

;