跨越变量初始化是不合法的还是会导致未定义的行为?

18

考虑以下代码:

void foo()
{
    goto bar;
    int x = 0;
    bar: ;
}

GCC和Clang拒绝它,因为跳到bar:会绕过变量初始化。MSVC根本没有抱怨(除了在bar:后使用x会引起警告)。

我们可以使用switch进行类似的操作:

void foo()
{
    switch (0)
    {
        int x = 0;
        case 0: ;
    }
}

现在所有三个编译器都会发出错误信息

这些片段是不正确的吗?还是会导致未定义行为?

我曾经认为两者都是不正确的,但我找不到标准的相关部分。 [stmt.goto] 对此没有任何说明,[stmt.select]也是一样。


2
如果你在跳转之后使用x,那么问题会变得更加微不足道。 - Jarod42
1
不是标准,但可以在此找到一些信息:https://en.cppreference.com/w/cpp/language/goto 特别是:“如果控制转移进入任何自动变量的作用域(例如通过跳过声明语句向前跳转),则程序是不合法的(无法编译),除非…” - 463035818_is_not_a_number
在MSVC中添加/permissive-标志,它也会抱怨。但我不知道没有该标志的MSVC行为是否定义良好(我认为应该是这样,否则为什么允许它?)。 - walnut
@walnut "否则他们为什么会允许呢" 可能是为了向后兼容性,或者因为他们并不太关心标准。所有主要的编译器在默认设置下都不符合标准。 - HolyBlackCat
https://dev59.com/k2s05IYBdhLWcg3wR_63#7334968 - Asteroids With Wings
2个回答

21

当初始化不是默认初始化时,就会导致其形式不正确。

[stmt.dcl]

3 可以通过跳转进入一个块,但不能绕过带有初始化的声明(包括条件语句和 init 语句)。 如果程序从一个变量的作用域外部跳转到它的作用域内部,而该变量具有自动存储期,则除非变量具有默认初始化([basic.life]),否则该程序是不合法的。在这种情况下,具有默认初始化的变量按其声明顺序构造。

初始化程序使初始化不是默认初始化。相比之下,这个

void foo()
{
    goto bar;
    int x; // no initializer
    bar: ;
}

将会是格式良好的。尽管使用具有不定值的x时通常要注意。


变量声明不是必须在作用域中的第一件事吗? - Cruncher
5
C89要求使用这个,但C++从未要求过,并且现代C也不再需要了。 - StoryTeller - Unslander Monica

4

来自goto语句

如果控制转移进入任何自动变量的作用域(例如通过跳过声明语句向前跳转),则程序是不正确的(无法编译),除非所有进入其作用域的变量均具有以下特征:

  1. 标量类型,未声明初始化器
  2. 具有平凡默认构造函数和平凡析构函数且未声明初始化器的类类型
  3. 上述类型之一的cv限定版本
  4. 上述类型之一的数组

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