最近,我读,在C采用灵活的阵列成员是贫穷的软件工程实践。 然而,这种说法是没有任何争论的支持。 这是一个公认的事实?
( 柔性阵列成员是在C99引入一个C特征,由此一个可以声明的最后一个元素是未指定的尺寸的阵列,例如:)
struct header {
size_t len;
unsigned char data[];
};
最近,我读,在C采用灵活的阵列成员是贫穷的软件工程实践。 然而,这种说法是没有任何争论的支持。 这是一个公认的事实?
( 柔性阵列成员是在C99引入一个C特征,由此一个可以声明的最后一个元素是未指定的尺寸的阵列,例如:)
struct header {
size_t len;
unsigned char data[];
};
这是一个公认的“事实”,使用goto语句较差的软件工程实践。 这并不表示它是真的。 有些时候,转到是很有用的,处理清理和从汇编器移植时尤其是当。
灵活的数组成员罢工我有一个主要用途,从我的头顶,这是遗留下来的数据格式,如在里斯科斯窗口模板格式。 他们会一直为这个超级有用大约15年前,我敢肯定还有人在那里处理这样的事情谁也发现它们非常有用。
如果使用灵活的数组成员是不好的做法,那么我建议大家都去告诉C99规范本的作者。 我怀疑他们可能有不同的答案。
我想给不这样做的原因是,它是不值得的,以配合您的代码以C99只是为了使用此功能。
问题的关键是,你可以随时使用下面的语句:
struct header {
size_t len;
unsigned char data[1];
};
这是完全便携。 然后,你可以分配用于在该阵列的n个元素的存储器时取1到帐户data
:
ptr = malloc(sizeof(struct header) + (n-1));
如果你已经有了C99的要求来构建你的代码用于其他任何原因或者你是针对特定的编译器,我看不出有任何的伤害。
你的意思是...
struct header
{
size_t len;
unsigned char data[];
};
在C语言中,这是一个常见的成语。 我想很多的编译器也接受:
unsigned char data[0];
是的,这是危险的,但话又说回来,这真是没有比普通C数组更危险 - 即,非常危险;-)。 小心只有在你真正需要的未知大小的数组的情况下使用它。 确保你malloc和正确地释放内存,使用类似: -
foo = malloc(sizeof(header) + N * sizeof(data[0]));
foo->len = N;
另一种方法是使数据仅仅是指向的元素。 然后,您可以realloc()的数据,以正确的尺寸要求。
struct header
{
size_t len;
unsigned char *data;
};
当然,如果你问有关C ++,无论这些是不好的做法。 然后,你通常使用STL向量来代替。
我见过这样的事情:从C接口和实现。
struct header {
size_t len;
unsigned char *data;
};
struct header *p;
p = malloc(sizeof(*p) + len + 1 );
p->data = (unsigned char*) (p + 1 ); // memory after p is mine!
注:数据不一定是最后一个成员。
不,使用灵活的数组成员在C不是不好的做法。
这种语言功能最早在ISO C99规范,6.7.2.1(16)。 对于当前的标准,ISO C11,它在第6.7.2.1(18)被指定。
您可以使用它们像这样:
struct Header {
size_t d;
long v[];
};
typedef struct Header Header;
size_t n = 123; // can dynamically change during program execution
// ...
Header *h = malloc(sizeof(Header) + sizeof(long[n]));
h->n = n;
另外,您也可以像这样分配的:
Header *h = malloc(sizeof *h + n * sizeof h->v[0]);
需要注意的是sizeof(Header)
包括最终填充字节,从而,下面的分配不正确,并且可以产生一个缓冲区溢出:
Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!
具有柔性阵列成员结构降低了分配用于它的1/2你只需要1含义较少的努力和由内存分配器簿记开销占用较少的存储器的数量,即,而不是2个分配用于一个结构对象。 此外,为您节省了一个额外的指针存储。 因此,如果你要分配大量的这种结构情况下,你可测量的提高你的程序的运行时间和内存使用(由一个常数因子)。
与此相反,使用非标准化的构建体为柔性阵列成员产生不确定的行为(例如,如在long v[0];
或long v[1];
显然是不好的做法。 因此,任何未定义行为这应该是可以避免的。
由于ISO C99是在1999年发布,大约20年前,争创ISO C89的兼容性是一个薄弱的论据。
作为一个侧面说明,对C89的兼容性,这样的结构应分配,如:
struct header *my_header
= malloc(offsetof(struct header, data) + n * sizeof my_header->data);
或与宏:
#define FLEXIBLE_SIZE SIZE_MAX /* or whatever maximum length for an array */
#define SIZEOF_FLEXIBLE(type, member, length) \
( offsetof(type, member) + (length) * sizeof ((type *)0)->member[0] )
struct header {
size_t len;
unsigned char data[FLEXIBLE_SIZE];
};
...
size_t n = 123;
struct header *my_header = malloc(SIZEOF_FLEXIBLE(struct header, data, n));
设置FLEXIBLE_SIZE到SIZE_MAX几乎确保这将失败:
struct header *my_header = malloc(sizeof *my_header);
有相关的结构是如何有时用一些缺点,它可以是危险的,如果你不认为通过的影响。
对于你的榜样,如果你开始的函数:
void test(void) {
struct header;
char *p = &header.data[0];
...
}
然后结果是不确定的(因为没有存储是有史以来分配数据)。 这是东西,你一般会知道的,但也有其中C程序员很可能取得用来能够使用值语义结构,打破各种其它方式下的情况。
举例来说,如果我定义:
struct header2 {
int len;
char data[MAXLEN]; /* MAXLEN some appropriately large number */
}
然后,我可以简单地复制通过转让,即两个实例:
struct header2 inst1 = inst2;
或者,如果他们被定义为指针:
struct header2 *inst1 = *inst2;
然而,这将不会工作,因为可变阵列data
不被复制过。 你需要的是动态的malloc结构的大小和复制与阵列上memcpy
或等价物。
同样,写一个接受结构将无法正常工作,因为在函数调用的参数是,又一个功能,通过复制值,这样的话你会得到什么很可能只是你的数组的第一个元素data
。
这并不能使一个坏主意使用,但你必须记住总是动态分配这些结构和大约只有将它们作为指针。