柔性数组

关于柔性数组…

1,什么是柔性数组?

先来了解一下“不完整类型(incomplete type)”,不完整类型是这样一种类型,它缺乏足够的信息(如长度)去描述一个完整的对象。C99标准支持不完整类型,其形式形如int a[],但也有一些编译器把int a[0] 作为非标准扩展来支持。

知道了不完整类型,就可以去了解柔性数组了。在日常的编程中,有时候需要在结构体中存放一个长度动态的字符串,一般的做法,是在结构体中定义一个指针成员,这个指针成员指向该字符串所在的动态内存空间,例如:

typedef struct test  
{  
       int a;  
       double b;  
       char *p;  
};

这样子是很容易想到的做法,但是却有一点不方便的地方。比如为test对象分配空间之后,还需要再为p指针分配一次空间。这样子,如果第二次malloc失败了,就必须要回滚释放第一个分配的结构体,这样带来了编码麻烦。

于是,有一种做法是,只进行一次分配。可以把代码修改为:

typedef struct test  
{  
       int a;  
       double b;  
       char p[0];   // 若有些编译器不支持,可以改成char p[];  
};

这样子进行分配的时候,只需要:

char a[] = "hello world";
test *stpTest = (test *)malloc(sizeof(test) + strlen( a ) + 1 );
strcpy(stpTest + 1, a );

释放结构体的时候,也不会出现忘记释放指针导致的内存泄露问题,因为只需要:free(stpTest);

在进行Linux内核开发或者嵌入式开发时,经常会遇到结构体的最后出现char data[], char data[0], char data[1],这样的代码,这就是柔性数组的实现。柔性数组也并没有定义柔性数组,只是所支持的不完整类型产生了柔性数组这样神奇的结构。

2,使用柔性数组的好处

其实上面也说到了,总结而来就是:
(1)不需要初始化,数组名直接就是所在的偏移
(2)不占任何空间,指针需要占用int长度空间,空数组不占任何空间。(注意,char data[1];这种形式是占用一个单位的空间的)
(3)空间一次分配,防止内存泄漏
(4)分配连续的内存,减少内存碎片化。(因为指针所分配的空间不是连续的,而数组占用连续的空间)

3,使用柔性数组时需要注意的

(1)必须是结构体的最后一个成员
(2)柔性数组之上,需要有其他的成员(结构体中不能只有一个柔性数组)
(3)sizefo返回的结构体的大小不包括柔性数组的内存(如果是char data[1]就会有一个单位的空间)

4,使用char data[0]与char data[1]的区别(转载的)

结构体中最后一个成员为[0]长度数组的用法:这是个广泛使用的常见技巧,常用来构成缓冲区。比起指针,用空数组有这样的优势:(1)、不需要初始化,数组名直接就是所在的偏移;(2)、不占任何空间,指针需要占用int长度空间,空数组不占任何空间。“这个数组不占用任何内存”,意味着这样的结构节省空间;“该数组的内存地址就和它后面的元素地址相同”,意味着无需初始化,数组名就是后面元素的地址,直接就能当指针使用。

这样的写法最适合制作动态buffer,因为可以这样分配空间malloc(sizeof(structXXX) + buff_len); 直接就把buffer的结构体和缓冲区一块分配了。用起来也非常方便,因为现在空数组其实变成了buff_len长度的数组了。这样的好处是:(1)、一次分配解决问题,省了不少麻烦。为了防止内存泄露,如果是分两次分配(结构体和缓冲区),那么要是第二次malloc失败了,必须回滚释放第一个分配的结构体。这样带来了编码麻烦。其次,分配了第二个缓冲区以后,如果结构里面用的是指针,还要为这个指针赋值。同样,在free这个buffer的时候,用指针也要两次free。如果用空数组,所有问题一次解决。(2)、小内存的管理是非常困难的,如果用指针,这个buffer的struct部分就是小内存了,在系统内存在多了势必严重影响内存管理的性能。要是用空数组把struct和实际数据缓冲区一次分配大块问题,就没有这个问题。如此看来,用空数组既简化编码,又解决了小内存碎片问题提高了性能。

结构体中最后一个成员为[1]长度数组的用法:与长度为[0]数组的用法相同,改写为[1]是出于可移植性的考虑。有些编译器不支持[0]数组,可将其改成[]或[1].

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

示例代码
#include<iostream>
using namespace std;

typedef struct _SoftArray {
	char ch;
	int arr[0];
}SoftArray;

int main() {
	cout << sizeof(SoftArray) << endl;
	
	const int LENGTH = 10;
	SoftArray* sa = (SoftArray*)malloc(sizeof(SoftArray) + sizeof(int) * LENGTH);

	for (int i = 0; i < LENGTH; i++) {
		sa->arr[i] = i;
	}

	for (int i = 0; i < LENGTH; i++) {
		cout << sa->arr[i] << " ";
	}
	cout<<endl;
	free(sa);
	return 0;
}

// result:
// 4
// 0 1 2 3 4 5 6 7 8 9


参考文献:
1、  http://blog.chinaunix.net/uid-26750459-id-3191136.html
2、  http://blog.csdn.net/ce123_zhouwei/article/details/8973073
3、 https://blog.csdn.net/fengbingchun/article/details/24185217


  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值