零长度数组 vs. 指针

22

编辑:显然,某些内容不允许/在各种C标准中已更改。 为了我的假设的好处,让我们假装我们正在使用没有标准或警告选项的gcc test.c

特别是我正在关注底层细节。 我已经添加了我目前的理解。 我是对的吗?

char **c1;   //Size for a pointer is allocated on the stack. sizeof(c1) == sizeof(void*)
char *c2[0]; //Nothing is allocated on the stack.  sizeof(c2) == 0

除了 sizeof 之外,这两种情况之间是否还有我不知道的其他区别?

struct a {
   int i;
   char c[0]; //sizeof(a) is sizeof(int)?  a.c == (&i)+1?
};

据我了解,这通常用于结构体末尾的可变长度数组。但是,那么对于

struct b {
   char *c[0] //sizeof(b) is 0?  where does c point?
};

int j;
struct b myb; //myb.c == (&j)+1 == $esp?

此外,如果一个零长度数组的指针没有被分配任何空间,它的地址如何被知晓? 我想它应该和常规数组一样,但我现在还无法理解。


请参见此处 - Shahbaz
5个回答

12

我曾经看到过使用零长度数组的唯一情况是当你想要一个可变长度的结构体时。

就像这个例子从这里选取的一样。

    struct line {
       int length;
       char contents[0];
     };

     struct line *thisline = (struct line *)
       malloc (sizeof (struct line) + this_length);
     thisline->length = this_length;

您不想在上述结构体中使用指针,因为您希望内容是结构体分配的内存的一部分。

如果对上述结构体执行sizeof操作,则不包括任何内容的空间。我不确定这是否已经成为标准,在各种编译器上会发出警告。


感谢您解释这个习语的实际用途。如果一个人试图理解使用它的代码,那么是否正确就不是关键了。 - Eric Walker

10

ISO C 禁止使用长度为 0 的数组。

char **c1;

这定义了一个类型为指向指针的指针的 c1 对象。

char *c2[0];

这是一个编译错误。不被允许的。无论是在C还是在C++中都不被允许。

struct a {
  int i;
  char c[0]; //sizeof(a) is sizeof(int)?  a.c == (&i)+1?
};

如前所述,出现错误--数组的大小必须大于零。

 struct a {
  int i;
  char c[1]; 
};

也被称为结构体技巧。在几乎所有操作系统上的低级代码中被滥用——Linux、Windows。

C99引入了一个更好地被称为灵活数组成员的无大小数组:

struct a {
  int i;
  char c[]; /* note: no size */ 
};

这并不总是编译时错误。只有在使用 -Wall 和 -pedantic 标志时,gcc 才会发出警告。 - Matt K
1
给定直接使用gcc编译,我的所有评估都是正确的吗(关于寻址和指针所指向的位置)? - jdizzle
1
@litb:我不同意这种理由,因为据我所知,编译器可以自由地假设如果一个数组声明大小为一,则对数组的任何访问都会访问第一个元素(即使在结构体末尾有分配空间,任何其他访问也将是未定义行为)。对于零大小的数组进行特殊处理(声明编译器不能做出关于“真实”大小的假设)似乎比要求程序员使用非标准的hack或猜测最大大小更加清晰。 - supercat
5
我觉得这个答案有点学究。问题提出者似乎想要了解为什么使用某个成语。无论C99是否支持该成语,都应该在完整的解释中加入这个有趣的细节。但事实上,一些代码(如Git)使用了它,人们想要理解这段代码的含义。 - Eric Walker
1
@EricWalker,同意。在GNU C中,零长度数组是完全有效的,因此这个答案感觉不完整。我曾经在Linux内核的Wi-Fi驱动程序代码中看到过这种用法。另外,请参见http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html。 - AriX
显示剩余8条评论

2

你可能会在GCC文档中找到一些答案。但它只关注将零长度数组作为结构体的最后一个成员的主题,对于sizeof并没有直接的答案。

(正如其他人所指出的那样,零长度数组不是标准C的一部分,但它们是GNU C的一部分。)


1

从C99标准(7.20.3)中,涉及到分配函数:

每个这样的分配都应产生一个指向与任何其他对象不相交的对象的指针。

[...]

如果请求空间的大小为零,则行为是实现定义的: 要么返回空指针,要么行为就像大小为某些非零值一样, 但返回的指针不得用于访问对象。

换句话说,在堆栈上声明b而没有初始化程序的情况下(其中b被声明为c[]而不是c[0]),实际使用的空间大小将会> 0,因为您无法访问b的任何部分。如果通过malloc分配它,它将作为0或无法访问的某些唯一值返回(如果您使用sizeof(b))。


这个标准的那一部分只适用于malloc,对于此答案较早的部分我看不出它的相关性。"space actually used will be > 0, since you cannot access any part of b"是什么意思? - Aaron McDaid

0

数组大小不能为零。

ISO 9899:2011 6.7.6.2:

If the expression is a constant expression, it shall have a value greater than zero.

上述文本对于普通数组(§1)和VLA(§5)都是正确的。这是C标准中规范的文本。编译器不允许以不同方式实现它。
使用gcc -std=c99 -pedantic会对此发出警告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接