当前位置:  开发笔记 > 编程语言 > 正文

在C练习中使用灵活的阵列成员?

如何解决《在C练习中使用灵活的阵列成员?》经验,为你挑选了5个好方法。

我最近读到在C中使用灵活的阵列成员是糟糕的软件工程实践.但是,该声明没有任何论据支持.这是公认的事实吗?

(灵活的数组成员是C99中引入的C特性,其中可以将最后一个元素声明为未指定大小的数组.例如:)

struct header {
    size_t len;
    unsigned char data[];
};

Airsource Lt.. 25

使用goto的软件工程实践很差,这是一个公认的"事实".这并不是真的.有时goto很有用,特别是在处理清理和从汇编程序移植时.

灵活的阵列成员让我觉得有一个主要用途,在我的头顶,这是在RiscOS上映射遗留数据格式,如窗口模板格式.在大约15年前,它们对于这一点非常有用,而且我确信仍有人在那里处理那些会发现它们有用的东西.

如果使用灵活的阵列成员是不好的做法,那么我建议我们都告诉作者C99规范这个.我怀疑他们可能有不同的答案.



1> Airsource Lt..:

使用goto的软件工程实践很差,这是一个公认的"事实".这并不是真的.有时goto很有用,特别是在处理清理和从汇编程序移植时.

灵活的阵列成员让我觉得有一个主要用途,在我的头顶,这是在RiscOS上映射遗留数据格式,如窗口模板格式.在大约15年前,它们对于这一点非常有用,而且我确信仍有人在那里处理那些会发现它们有用的东西.

如果使用灵活的阵列成员是不好的做法,那么我建议我们都告诉作者C99规范这个.我怀疑他们可能有不同的答案.


当我们想要在递归可能会增加编译器的额外开销的情况下使用非递归实现来实现算法的递归实现时,goto也很有用.
@pranavk你可能应该使用`while`,然后.
网络编程是另一种,您将标头作为结构,并将数据包(或在您所在的层中称为的包)作为灵活数组。调用下一层,剥离标头,然后传递数据包。对网络堆栈中的每个层执行此操作。(您将下层的数据从下层恢复到您所在的层的结构)

2> Remo.D..:

我不赞成的原因是,为了使用这个功能,将代码绑定到C99是不值得的.

关键是你总是可以使用以下习语:

struct header {
  size_t len;
  unsigned char data[1];
};

这是完全便携的.然后,在为数组中的n个元素分配内存时,可以考虑1 data:

ptr = malloc(sizeof(struct header) + (n-1));

如果由于任何其他原因已经将C99作为构建代码的要求,或者您的目标是特定的编译器,我认为没有坏处.


"以下成语"不是完全可移植的,这就是为什么灵活的阵列成员被添加到C99标准的原因.
在C99中使用`unsigned char data [1]`是不可移植的,因为`((header*)ptr) - > data + 2` - 即使分配了足够的空间 - 创建一个指向length-1之外的指针数组对象(而不是结尾的哨兵).但是根据C99 6.5.6p8,"如果指针操作数和结果都指向同一个数组对象的元素,或者一个超过数组对象的最后一个元素,则评估不应产生溢出;**否则,行为未定义**"(强调添加).灵活的数组(6.7.2.2p16)就像一个数组填充分配的空间,不会在这里命中UB.
***警告:**使用`[1]`已导致GCC生成错误的代码:https://lkml.org/lkml/2015/2/18/407
@ Remo.D:小点:`n-1`没有准确地考虑额外的分配,因为对齐:在大多数32位机器上,sizeof(struct header)将是8(保持4的倍数,因为它有一个32位字段,它更喜欢/需要这样的对齐)."更好"的版本是:`malloc(offsetof(struct header,data)+ n)`
谢谢.我离开了n-1,因为它可能不会被用作字符串.
乔纳森的观点得到了委员会(或至少一些成员)的赞同,但它与事实相矛盾.当一起考虑时,标准**的其他几个部分需要**旧的[1]`技巧和`[]`一样工作(除了可能浪费一些额外的存储字节).
[CERT安全编码标准](https://www.securecoding.cert.org/confluence/display/seccode)不建议使用此成语:"[MEMxx-C.了解如何使用灵活的阵列成员](https ://www.securecoding.cert.org/confluence/display/seccode/MEMxx-C.+Understand+how+flexible+array+members+are+to+be+used)" - "使用这种方法的问题是访问除第一个数据元素以外的行为是未定义的"
@R ..我没有一个实际的反例,声称`[1]`必须和`[]`一样工作,但是这篇文章中的第一个例子很接近:GCC(甚至不是特别是最近的版本假设对数组成员的访问保留在数组中.由于礼貌,GCC并不认为`q-> tab [2]`是第二个例子中无法达到的表达式.http://blog.frama-c.com/index.php?post/2013/07/31/From-Pascal-strings-to-Python-tuples
@supercat都不赞成。在C89中,它们是在某些情况下合理的骇客。在C99中,专门引入了Flexible Array Member,以提供一个定义明确的工具,使所有这些黑客都变得不必要。旧代码应迁移。FAM具有不完整类型的优点,因此不可能意外地将`sizeof'应用于它
**警告**:当使用`gcc -mmpx -fcheck-pointer-bounds`时,使用`[1]`将导致绑定违规报告。

3> Roddy..:

你的意思是...

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向量.


命名一个接受零长度数组的编译器.(如果答案是GCC,现在命名为另一个.)它不受C标准的制裁.
C++但没有STL ......这不是一个愉快的想法!
我曾在C++中工作但没有STL环境 - 我们有自己的容器,它提供了常用的功能,而没有STL迭代器系统的完整通用性.它们更容易理解并且具有良好的性能.但是,这是在2001年.
只要您在支持STL的系统上编码!

4> Nyan..:

我见过这样的东西:从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! 

注意:数据不必是最后一个成员.


实际上,这有一个优点,即`data`不一定是最后一个成员,但每次使用`data`时它也会产生额外的解引用.灵活的数组使用与主结构指针的常量偏移来替换该去引用,这在一些特别常见的机器上是免费的,而在其他地方则便宜.

5> maxschlepzig..:

不,在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个分配,这意味着更少的工作量和更少的内存分配器记账开销。此外,您还为另一个指针保存了存储空间。因此,如果必须分配大量这样的结构实例,则可以通过一定的因素来显着提高程序的运行时和内存使用率。

与此相反,对产生不确定行为(例如in long v[0];long v[1];)的灵活数组成员使用非标准化构造显然是不好的做法。因此,应避免任何不确定的行为。

自从将ISO C99于1999年发布以来(即20年前),争取ISO C89兼容性一直是一个薄弱的论点。

推荐阅读
女女的家_747
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有