非数组的 C 花括号初始化器

问题描述 投票:0回答:2

请考虑以下荒谬但编译的代码:

int main(int argc, char *argv[]) {
    void *a, *b, *c, *d;

    void *the_good_array[] = { a, b, c, d };
    void *the_bad_array    = { a, b, c, d };

    return 0;
}

我无意中生成了类似的东西,令我惊讶的是,它完全编译通过了。

  • 为什么会编译?
  • 作为非数组的初始值设定项,
    { a, b, c, d }
    意味着什么?如果没有大括号,我会理解这是意外使用
    ,
    运算符导致最后一个元素的值 (
    d
    )。但是,牙套?他们在这里是什么意思?

如果可以的话,回答时请参考C标准。

编辑1: 与两者一起编译:

gcc -std=iso9899:1990 /tmp/bad_array.c     # gcc (Debian 12.2.0-14)`
clang -std=iso9899:1990 /tmp/bad_array.c   # Debian clang version 14.0.6

编辑2: 显然

int i = { 0 };

完全有效 C.

我想知道为什么,但没有阅读的标准。

c language-lawyer
2个回答
0
投票

作为非数组的初始值设定项,{ a, b, c, d } 意味着什么?

单个对象具有多个初始化器元素是违反约束的。 N2176(C17 标准草案)§6.7.9 初始化:

限制

  1. 任何初始值设定项都不得尝试为不包含在实体中的对象提供值 已初始化。

在这种情况下,它取决于编译器会发生什么。

为什么会编译?

C 编译器非常重视向后兼容性,并且会接受许多现在被认为无效的代码。

但是,牙套?他们在这里是什么意思?

即使在单个表达式周围也允许使用大括号:

语法

    • 初始化器:
      • 赋值表达式
      • { 初始化列表 }
      • { 初始化列表 , }
    • 初始化列表:
      • 指定opt 初始化器
      • 初始化器列表指定opt初始化器
        ...

0
投票

任何初始化器都可能有大括号。 你可以忽略它们。来自 https://port70.net/~nsz/c/c11/n1570.html#6.7.9p11 :

标量的初始值设定项应为单个表达式,可以选择用大括号括起来。

示例:

int i = {1};

编译器通知您:

<source>:5:35: warning: excess elements in scalar initializer [-Wexcess-initializers]
    5 |     void *the_bad_array    = { a, b, c, d };

看起来编译器选择忽略“多余”,只采用

a

为什么会编译?

简单的回答是,编译器是人们按照编译的方式创建的。 GCC 编译器编写者选择以这种特殊方式处理标量初始值设定项中的多余元素。

底线,代码无效。它打破了上面链接中的“应该”:

标量的初始值设定项应为单个表达式

https://port70.net/~nsz/c/c11/n1570.html#4p2我们知道:

如果违反了约束或运行时约束之外的“应”或“不应”要求,则行为未定义。

行为没有定义,这意味着任何事情都可能发生。

© www.soinside.com 2019 - 2024. All rights reserved.