如果我在C / C ++中定义0大小的数组会发生什么?

时间:2012-03-15 15:12:43

标签: c++ c arrays

好奇,如果我在代码中定义零长度数组int array[0];会发生什么?海湾合作委员会根本没有抱怨。

示例程序

#include <stdio.h>

int main() {
    int arr[0];
    return 0;
}

澄清

我实际上是在试图弄清楚零长度数组是否以这种方式初始化,而不是像Darhazer评论中的变量长度一样被指出,是否优化了。

这是因为我必须释放一些代码,所以我想弄清楚我是否必须处理SIZE被定义为0的情况,这发生在一些静态定义的代码int array[SIZE];

我真的很惊讶海湾合作委员会没有抱怨,这导致了我的问题。根据我收到的答案,我认为缺少警告主要是因为支持旧代码,而这些代码尚未使用新的[]语法进行更新。

因为我主要想知道这个错误,所以我将Lundin的答案标记为正确(Nawaz是第一个,但它并不完整) - 其他人指出它实际用于尾部填充结构,而相关,并不是我想要的。

7 个答案:

答案 0 :(得分:78)

数组的大小不能为零。

ISO 9899:2011 6.7.6.2:

  

如果表达式是常量表达式,则其值应大于零。

以上文本对于普通数组都是正确的(第1段)。对于VLA(可变长度数组),如果表达式的值小于或等于零,则行为未定义(第5段)。这是C标准中的规范性文本。不允许编译器以不同方式实现它。

gcc -std=c99 -pedantic对非VLA案件发出警告。

答案 1 :(得分:73)

通常情况下,不允许这样做。

然而,目前在C语言中使用灵活数组

  

C99 6.7.2.1,§16:作为一种特殊情况,具有多个命名成员的结构的最后一个元素可能具有不完整的数组类型;这被称为灵活的阵列成员。

演示:

struct Array {
  size_t size;
  int content[];
};

这个想法是你会这样分配它:

void foo(size_t x) {
  Array* array = malloc(sizeof(size_t) + x * sizeof(int));

  array->size = x;
  for (size_t i = 0; i != x; ++i) {
    array->content[i] = 0;
  }
}

您也可以静态使用它(gcc扩展名):

Array a = { 3, { 1, 2, 3 } };

这也称为尾部填充结构(此术语早于C99标准的发布)或 struct hack (感谢Joe Wreschnig指出)

然而,这种语法最近才在C99中标准化(并保证了效果)。在需要恒定尺寸之前。

  • 1是可移植的方式,虽然它很奇怪
  • 0更能表明意图,但就标准而言并不合法,并且作为某些编制者(包括gcc)的延伸支持

然而,尾部填充练习依赖于存储可用(谨慎malloc)这样的事实,因此不适合来堆叠使用。

答案 2 :(得分:57)

在标准C和C ++中,零大小的数组允许..

如果您正在使用GCC,请使用-pedantic选项进行编译。它将提供警告,说:

  

zero.c:3:6: warning: ISO C forbids zero-size array 'a' [-pedantic]

对于C ++,它会发出类似的警告。

答案 3 :(得分:24)

这完全是非法的,而且一直都是,但很多编译器 忽视发出错误的信号。我不确定你为什么要这样做。 我所知道的一个用途是从布尔值触发编译时错误:

char someCondition[ condition ];

如果condition为false,则会出现编译时错误。因为 编译器允许这样做,但是,我已经开始使用:

char someCondition[ 2 * condition - 1 ];

这给出了1或-1的大小,我从未找到过编译器 这将接受-1的大小。

答案 4 :(得分:8)

我将在此论点中添加gcc的联机文档whole page

一些引言:

  

GNU C中允许使用零长度数组。

  

在ISO C90中,您必须为内容提供1

的长度

  

3.0之前的GCC版本允许静态初始化零长度数组,就好像它们是灵活的数组一样。除了那些有用的案例之外,它还允许在破坏以后数据的情况下进行初始化

所以你可以

int arr[0] = { 1 };

和繁荣: - )

答案 5 :(得分:6)

如果允许结构中的零大小数组声明将是有用的,并且如果语义是这样的(1)它们将强制对齐但是否则不分配任何空间,并且(2)索引数组将被认为是定义的在结果指针与结构体位于同一内存块内的情况下的行为。任何C标准都不允许这样的行为,但是一些较旧的编译器在它成为编译器的标准之前允许它允许使用空括号的不完整数组声明。

结构黑客,通常使用大小为1的数组实现,是狡猾的,我不认为有任何要求编译器不要破坏它。例如,我希望如果编译器看到int a[1],那么将a[i]视为a[0]是有权利的。如果有人试图通过类似

之类的东西解决结构黑客的对齐问题
typedef struct {
  uint32_t size;
  uint8_t data[4];  // Use four, to avoid having padding throw off the size of the struct
}

编译器可能会变得聪明并假设数组大小确实为4:

; As written
  foo = myStruct->data[i];
; As interpreted (assuming little-endian hardware)
  foo = ((*(uint32_t*)myStruct->data) >> (i << 3)) & 0xFF;

这样的优化可能是合理的,特别是如果myStruct->data可以在与myStruct->size相同的操作中加载到寄存器中。我在标准中一无所知会禁止这样的优化,但当然它会破坏任何可能期望访问超出第四个元素的代码。

答案 6 :(得分:2)

按照标准,你绝对不能拥有零大小的数组,但实际上每个最流行的编译器都让你这样做。所以我会试着解释为什么它会很糟糕

#include <cstdio>

int main() {
    struct A {
        A() {
            printf("A()\n");
        }
        ~A() {
            printf("~A()\n");
        }
        int empty[0];
    };
    A vals[3];
}

我就像一个人会期望这样的输出:

A()
A()
A()
~A()
~A()
~A()

Clang 打印:

A()
~A()

GCC 打印:

A()
A()
A()

这很奇怪,所以如果可以的话,最好不要在 C++ 中使用空数组。

GNU C 中也有扩展,它使您可以在 C 中创建零长度数组,但按照我的理解,结构中应该至少有一个成员,否则,如果您使用 C++,您将得到上述非常奇怪的示例。