14

显然,具有灵活数组成员的结构不打算被声明,而是与指向该结构的指针一起使用。声明一个灵活数组成员时,必须至少有一个其他成员,并且该灵活数组成员必须是该结构中的最后一个成员。

假设我有一个看起来像这样的:

struct example{
    int n;
    int flm[]; 
}

然后要使用它,我必须声明一个指针并使用 malloc 为结构的内容保留内存。

struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int)); 

也就是说,如果我希望我的 flm[] 数组保存五个整数。然后,我可以像这样使用我的结构:

ptr->flm[0] = 1; 

我的问题是,我不应该只使用指针而不是这个吗?它不仅兼容 C99 之前的版本,而且我可以在有或没有指向该结构的指针的情况下使用它。考虑到我已经必须在 flm 中使用 malloc,难道我不应该这样做吗?

考虑一下示例结构的这个新定义;

struct example{
    int n; 
    int *notflm; 
}

struct example test = {4, malloc(sizeof(int) * 5)}; 

我什至可以像灵活数组成员一样使用替换:

这也行吗?(用notflm提供了上面例子的定义)

struct example test; 
test.n = 4; 
notflm = malloc(sizeof(int) * 5); 
4

2 回答 2

26

指针不是数组。选择使用哪个的基本原因与数组与指针的基本原因相同。在灵活数组成员的特殊情况下,以下是您可能更喜欢它们而不是指针的一些原因:

  • 减少存储需求。指针会将您的结构扩大(通常)4 或 8 个字节,如果您单独分配指向的存储空间而不是单独调用malloc.

  • 提高访问效率。一个灵活的阵列成员位于从结构基础的恒定偏移处。指针需要单独的取消引用。这会影响访问它所需的指令数量和注册压力。

  • 分配成功/失败的原子性。如果您分配结构并为其分配存储以指向两个单独的步骤,那么在失败情况下清理的代码将更加难看,因为您遇到了一个成功而另一个失败的情况。这可以通过一些指针算法从同一个malloc请求中分割出来来避免,但是由于对齐问题很容易导致逻辑错误并调用 UB。

  • 避免需要深拷贝。如果您使用灵活数组而不是指针,您可以简单地 memcpy (不分配,因为分配不知道灵活数组长度)来复制结构,而不必复制指向的数据并修复指针在新副本中。

  • 避免需要深度释放。能够只处理free单个对象而不是必须free指向数据是非常方便和干净的。当然,这也可以通过malloc上面提到的“分割单个”方法来实现,但是灵活的数组使其更容易且不易出错。

  • 当然还有很多原因...

于 2013-08-01T18:35:59.593 回答
0

正如您自己指出的那样,这些概念绝对没有必要。

您演示的两者之间的区别在于您的数据在内存中的位置。

在灵活数组的第一个示例中,您的元数据和数组本身位于同一个内存块中,如果需要,可以作为一个块(指针)移动。

在第二个示例中,您的元数据位于堆栈上,而您的数组位于堆的其他位置。为了移动/复制它,您现在需要移动两个内存块并更新元数据结构中的指针。

当您需要将数组及其元数据在空间上一起放置在内存中时,通常会使用灵活大小的数组。

这绝对有用的一个例子是,例如将一个数组及其元数据放在一个文件中 - 你只有一个连续的内存块,每次加载它时(很可能)它会被放置在 VM 的不同位置.

于 2013-08-01T18:36:09.210 回答