【问题标题】:Another way of implementing zero-length arrays?另一种实现零长度数组的方法?
【发布时间】:2016-01-14 16:53:04
【问题描述】:

我正在阅读有关 zero-length arrays 的信息,以及它的使用地点和方式以及所有内容。而且我知道当人们想要一个动态大小的成员在一个结构中时使用它们。它与使用指针不同,因为

  • 它允许您为结构分配内存,并为结构末尾的可变长度数组分配一个连续的内存块。
  • 如果您使用指针,则必须单独分配内存(两次 malloc 调用,可能不连续)或使用其他技巧(以实现正确对齐等)。

取自answer


现在,这一切都很好。直到我阅读了有关此问题的更多旧事实。

事实 1

  • 在 ISO C90 中,您必须将内容的长度设为 1,这意味着要么浪费空间,要么使 malloc 的参数复杂化。

事实 2

  • GCC 允许将此功能作为扩展。

现在我真的不知道扩展在这里意味着什么。但我在想的是Fact 1前面有更多解释


让我们看一些代码。
#include<stdio.h>
#include<stdlib.h>

struct line
{
    int len;
    char* content;
};

int main()
{

    int i;
    struct line* p = malloc(sizeof(struct line) + 10);
    p->len = 10;
    p->content = (char*)(&(p->len) + 1);

    printf("%p\n", &(p->len));

    for(i = 0; i < 10; i++)
    {
        printf("%p\n", &(p->content[i]));
    }

}

我想出的这段代码有效地实现了零长度数组的所有功能。可能是多了一行代码,但肯定比Fact 1的坏影响好。

所以,我的问题是,是否有任何特殊原因导致不使用上述方式(在上面的代码中演示)并且他们必须向 GCC 添加扩展,或者使用大小为 1 的数组。

【问题讨论】:

  • 零长度数组或 1 个元素的数组确保正确的对齐约束。
  • p-&gt;content = (char*)(&amp;(p-&gt;len) + 1); 应该只是 p-&gt;content = (char *) (p + 1); 以跳过结构。
  • @mikedu95,据我了解,上面的代码也会保证对齐。
  • @Haris 不,您不想要指向自身的指针。它应该指向结构体之后的第一个(对齐的)字节。
  • 没有。对于编译器来说,p-&gt;content 是一个指针,这意味着它必须取消对它的引用才能获得该值。如果您将该空间用于行的第一个字符,则将使指针无效。如果该字段是一个数组,p-&gt;content 将只是指向结构内数组的第一个元素的指针。数组不是声明它们的范围内的指针。它们只有在传递给函数时才会衰减为指针。

标签: c pointers gcc struct


【解决方案1】:

这种方法在len &gt; 0时不起作用。

它在content字段中保存了2个东西,数组的地址和数组的第一个元素。

重写代码来演示效果。

#include<stdio.h>
#include<stdlib.h>

struct line
{
    int len;
    char* content;
};

int main()
{

    int i;
    size_t size = sizeof(struct line) + 10;
    struct line* p = malloc(sizeof(struct line) + 10);
    memset(p, 0, size);
    p->len = 10;
    p->content = (char*)(&(p->len) + 1);

    printf("%p\n", &(p->len));

    for(i = 0; i < 10; i++)
    {
        printf("%p %02hhX\n", (void*) &(p->content[i]), p->content[i]);
    }

}

输出

0x80028260
0x80028264 64  // content address takes up same space as char array
0x80028265 82
0x80028266 02
0x80028267 80
0x80028268 00
0x80028269 00
0x8002826a 00
0x8002826b 00
0x8002826c 00
0x8002826d 00

【讨论】:

  • 哦,是的。我得到了它。但是随后 unwind 建议的内容将完美运行,因为这将使content 指向指针content 本身存储在结构中的位置旁边的内存。看到这个 --> ideone.com/ZHQw1Q
  • @Haris,这个修改想法是正确的,但是修改后的代码占用的空间(sizeof 指针)比真正需要的要多。所以清晰度与效率。空间需求真的很小:size_t size = sizeof(p-&gt;len) + 10*sizeof(*(p-&gt;content));(暂时忽略食物问题)
  • 是的,这是真的。它确实需要更多空间。用于存储指针的额外空间。
  • @Haris:您的示例有效,但您可能知道,除了可能浪费 1 个字符之外,您现在浪费了额外指针的空间并产生额外取消引用的成本。我不会为此担心太多。您不太可能在客户端代码中管理内存。使用单项数组的可移植标准方法并编写一个很好的构造函数来为您进行内存计算。
  • @MOehm,是的。我现在明白了。 :)
【解决方案2】:

您声明 char content[1] 而不是您的方法的原因是为了保持您的结构直接可序列化和可反序列化。想想如果您通过网络套接字编写line 结构或写入文件以供另一个应用程序读取会发生什么。 line-&gt;content 指针对于接收应用程序将完全无效。

如果将内容声明为大小为 1 的数组,则不存在上述问题。但是,您必须稍微复杂化您的malloc 调用。

【讨论】:

  • 为什么line-&gt;content指针对于接收应用程序完全无效?它以一个连续的块在内存中正确布局。
  • 因为当接收应用程序mallocs 空间来保存结构时,它被存储在与源应用程序相同的内存地址的机会非常低。请记住,content 字段占用 4 个(或 64 位为 8 个)字节空间来存储紧跟结构的内存地址。此内存地址在源应用程序和接收应用程序之间(很可能)会有所不同。
  • 好的,我现在明白你想说什么了。这也是一个有效的观点。但是发送应用程序必须发送len,然后从content指针读取并发送char,而不是发送整个结构。
  • @epicbrew:即使使用灵活的数组成员,您仍然需要计算malloc 有多少内存。将sizeof 应用于这样的结构会省略数组成员的大小。
  • @KeithThompson 是的,这是真的,我之前的评论并不意味着您可以忽略计算 malloc 调用的尾随数组的大小。现在重读它,我可以看到我的上述评论可能会令人困惑。我可能应该删除它。
猜你喜欢
  • 2021-06-05
  • 2012-09-06
  • 1970-01-01
  • 2017-04-06
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2010-09-22
  • 1970-01-01
相关资源
最近更新 更多