71 c arrays data-structures flexible-array-member
我最近读到在C中使用灵活的阵列成员是糟糕的软件工程实践.但是,该声明没有任何论据支持.这是公认的事实吗?
(灵活的数组成员是C99中引入的C特性,其中可以将最后一个元素声明为未指定大小的数组.例如:)
struct header {
size_t len;
unsigned char data[];
};
Run Code Online (Sandbox Code Playgroud)
Air*_*Ltd 25
使用goto的软件工程实践很差,这是一个公认的"事实".这并不是真的.有时goto很有用,特别是在处理清理和从汇编程序移植时.
灵活的阵列成员让我觉得有一个主要用途,在我的头顶,这是在RiscOS上映射遗留数据格式,如窗口模板格式.在大约15年前,它们对于这一点非常有用,而且我确信仍有人在那里处理那些会发现它们有用的东西.
如果使用灵活的阵列成员是不好的做法,那么我建议我们都告诉作者C99规范这个.我怀疑他们可能有不同的答案.
Rem*_*o.D 19
我不赞成的原因是,为了使用这个功能,将代码绑定到C99是不值得的.
关键是你总是可以使用以下习语:
struct header {
size_t len;
unsigned char data[1];
};
Run Code Online (Sandbox Code Playgroud)
这是完全便携的.然后,在为数组中的n个元素分配内存时,可以考虑1 data
:
ptr = malloc(sizeof(struct header) + (n-1));
Run Code Online (Sandbox Code Playgroud)
如果由于任何其他原因已经将C99作为构建代码的要求,或者您的目标是特定的编译器,我认为没有坏处.
Rod*_*ddy 10
你的意思是...
struct header
{
size_t len;
unsigned char data[];
};
Run Code Online (Sandbox Code Playgroud)
在C中,这是一个常见的习语.我想很多编译器也接受:
unsigned char data[0];
Run Code Online (Sandbox Code Playgroud)
是的,这很危险,但是再一次,它确实没有比普通C阵列更危险 - 即非常危险;-).小心使用它,并且仅在您真正需要未知大小的数组的情况下使用它.确保你使用malloc并使用以下内容正确释放内存: -
foo = malloc(sizeof(header) + N * sizeof(data[0]));
foo->len = N;
Run Code Online (Sandbox Code Playgroud)
另一种方法是使数据只是指向元素的指针.然后,您可以根据需要将数据realloc()重新设置为正确的大小.
struct header
{
size_t len;
unsigned char *data;
};
Run Code Online (Sandbox Code Playgroud)
当然,如果你问的是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!
Run Code Online (Sandbox Code Playgroud)
注意:数据不必是最后一个成员.
不,在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;
Run Code Online (Sandbox Code Playgroud)
另外,您可以像这样分配它:
Header *h = malloc(sizeof *h + n * sizeof h->v[0]);
Run Code Online (Sandbox Code Playgroud)
请注意,sizeof(Header)
其中包括最终的填充字节,因此,以下分配是错误的,并且可能会导致缓冲区溢出:
Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!
Run Code Online (Sandbox Code Playgroud)
具有灵活数组成员的结构将其分配数量减少1/2,即,您只需要1就可以为一个结构对象分配2个分配,这意味着更少的工作量和更少的内存分配器记账开销。此外,您还为另一个指针保存了存储空间。因此,如果必须分配大量这样的结构实例,则可以通过一定的因素来显着提高程序的运行时和内存使用率。
与此相反,对产生不确定行为(例如in 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);
Run Code Online (Sandbox Code Playgroud)
或使用宏:
#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));
Run Code Online (Sandbox Code Playgroud)
将 FLEXIBLE_SIZE 设置为 SIZE_MAX 几乎可以确保这将失败:
struct header *my_header = malloc(sizeof *my_header);
Run Code Online (Sandbox Code Playgroud)
有时使用结构体的方式有一些缺点,如果您不仔细考虑其含义,可能会很危险。
对于您的示例,如果您启动一个函数:
void test(void) {
struct header;
char *p = &header.data[0];
...
}
Run Code Online (Sandbox Code Playgroud)
然后结果是未定义的(因为没有为数据分配存储空间)。这是您通常会意识到的事情,但在某些情况下,C 程序员可能习惯于能够对结构使用值语义,而这会以其他各种方式失效。
例如,如果我定义:
struct header2 {
int len;
char data[MAXLEN]; /* MAXLEN some appropriately large number */
}
Run Code Online (Sandbox Code Playgroud)
然后我可以简单地通过赋值复制两个实例,即:
struct header2 inst1 = inst2;
Run Code Online (Sandbox Code Playgroud)
或者,如果它们被定义为指针:
struct header2 *inst1 = *inst2;
Run Code Online (Sandbox Code Playgroud)
然而,这不适用于灵活的数组成员,因为它们的内容不会被复制。您想要的是动态地分配结构的大小并使用memcpy
或等效的方法复制数组。
struct header3 {
int len;
char data[]; /* flexible array member */
}
Run Code Online (Sandbox Code Playgroud)
同样,编写接受 a 的函数struct header3
将不起作用,因为函数调用中的参数再次按值复制,因此您将获得的可能只是灵活数组成员的第一个元素。
void not_good ( struct header3 ) ;
Run Code Online (Sandbox Code Playgroud)
这并不是一个坏主意,但您必须记住始终动态分配这些结构,并且仅将它们作为指针传递。
void good ( struct header3 * ) ;
Run Code Online (Sandbox Code Playgroud)