Visual studio 将不完整的数组视为零长度数组

Visual studio treats incomplete arrays as zero-length arrays

我有一个看起来像这样的结构:

typedef struct foo {
    int this;
    int that;
    int length;
    int info[];     // legal for last element of a struct 
} Foo;

当我编译它时,我收到这样的警告:

C4200 nonstandard extension used: zero-sized array in struct/union

我只是忍受警告,还是有一些 属性 我可以设置告诉 Visual Studio 使用 C-99?

当您创建一个未定义长度的数组(在您的例子中是一个结构)时,不知道需要为其分配多少内存。因此,当使用变量 'info' 时,程序正在写入或读取计算机高速缓存中的哪个位置是未知的。这会使程序崩溃。通常,当您想使用未定义长度的数组时,会使用指针。当知道数组应该有多大时,可以为这个指针分配内存。

编译器会警告您我上面描述的问题,但允许您执行代码(至少在 Microsoft Visual studio 2013 中是这样)。

希望对您有所帮助!

对于那些对 zero-length 数组或 "flexible array" 习语感到好奇的人,可能值得花一点时间来解释它们。这个习语和 C 本身一样古老。

假设您要传递一个由 header 和可变数据量组成的结构。在分配结构之前不知道需要向其中添加多少数据。

原来的成语是这样声明结构的:

/* Variation 1 */
struct mydata {
    int type;
    int datalen;
    char data[1];
};

然后假设我们想要 return 其中之一 objects:

struct mydata *
get_some_data()
{
    int len;
    struct mydata *rval;
    len = find_out_how_much_data();
    /* Allocate the struct AND enough extra space to hold the data */
    rval = malloc(sizeof(*rval) + len - 1);
    rval->datalen = len;
    read_data(&rval->data[0], len);
    return rval;
}

调用者会像这样访问它:

void caller()
{
    struct mydata *foo = get_some_data();
    /* Start accessing foo->datalen bytes of data starting at
     * foo->data[0]
     */
    free(foo);     /* And free it all */
}

这个成语的意思是 char data[1] 声明是一个谎言,因为数据肯定会比那更长,但 C 编译器不进行范围检查,所以一切都很酷。

但是请注意 malloc 中的 len - 1 表达式。这是必要的,因为声明数据的长度为 1 会在所有内容中引入 off-by-one 错误,并邀请编码人员犯错误。

因此 GNU 和 Microsoft 都对语言添加了扩展,允许您声明长度为零的数组:

/* Variation 2 */
struct mydata {
    int type;
    int datalen;
    char data[0];
};

虽然从表面上看,这是荒谬的,但它与这里使用的成语非常吻合。现在我们可以简单地做:

rval = malloc(sizeof(*rval) + len);

而且代码更简洁。

C99 通过承认数组的长度是一个谎言来形式化这个习语,但是在结构末尾有额外数据的能力非常方便。所以现在你声明:

/* Variation C99 */
struct mydata {
    int type;
    int datalen;
    char data[];
};

并且所有内容的编码都与 Gnu/Microsoft 扩展名完全相同。

不幸的是,Microsoft 似乎并未在其编译器中采用 C99 标准,因此无论您做什么,变体 2 和 C99 都会生成警告。看起来我唯一的选择是接受警告消息或添加 pragma 来抑制它。

Linux 用户可以通过执行 gr -r '\[0\]' /usr/include 并查看 许多 地方如何使用 zero-length 数组来自娱自乐。这是一个很常用的成语。

关于我自己的问题:我正在使用的结构实际上是 ioctl 的一部分。 driver 已经写入,无法更改。我最多能做的就是将数组从 zero-length 重新定义为 flexible。不幸的是,这两个选项都不会让 MSVC 编译器满意。

Visual Studio 2015 [几乎] 完全实现了 C99,但仍然将所有 C99 功能视为 语言扩展 (例如,禁用语言扩展也会禁用 C99 支持)。其中一些功能会触发虚假警告,就像您观察到的那样。

只要 C99 支持保持这种半官方 "extension" 状态,就只是 ignore/disable 这样的警告。

请注意,VS2015 Update 3 不再对此类 C 代码发出此警告。