【问题标题】:"crosses initialization of variable" only when initialization combined with declaration仅在初始化与声明相结合时“跨越变量的初始化”
【发布时间】:2012-10-11 02:55:59
【问题描述】:

我已阅读 this question 关于“跳转到案例标签”错误的信息,但我仍有一些问题。我在 Ubuntu 12.04 上使用 g++ 4.7。

此代码给出错误:

int main() {
  int foo = 1;
  switch(foo) {
  case 1:
    int i = 0;
    i++;
    break;
  case 2:
    i++;
    break;
  }
}

错误是

jump-to-case-label.cpp: In function ‘int main()’:
jump-to-case-label.cpp:8:8: error: jump to case label [-fpermissive]
jump-to-case-label.cpp:5:9: error:   crosses initialization of ‘int i’

但是,这段代码编译得很好,

int main() {
  int foo = 1;
  switch(foo) {
  case 1:
    int i;
    i = 0;
    i++;
    break;
  case 2:
    i++;
    break;
  }
}

第二个代码比第一个代码危险吗?我对为什么 g++ 允许它感到困惑。

其次,解决这个问题的方法是限定初始化变量的范围。如果初始化的变量是一个大对象,而switch语句在while循环中,会不会每次进入和离开那个scope都会调用构造函数和析构函数,导致效率下降?或者编译器会优化它吗?

【问题讨论】:

  • 我不明白为什么会编译。如果您直接进入案例 2,您的 i 甚至还没有被声明!
  • (“次要”错误消息似乎与问题更相关。)
  • 投反对票的人能否解释一下他们的投反对票?
  • @alestanis:为什么你认为i 还没有被声明?它在case 1: 标签之后的行中明确定义。 break 语句 不要 神奇地将块拆分为不同的范围。这样做没有任何意义,因为如果程序员选择这样做,它们可以有条件地执行。

标签: c++ performance g++ switch-statement


【解决方案1】:

跳过对象的初始化,即使对象是int 类型,也始终是未定义的行为。请注意,switch-statement 的声明并没有什么特别之处:它只是一个声明,人们已经 [ab-] 使用了这种有趣的方式,例如 Duff's Device。声明中唯一特别的是标签可以采用default:case <const-integer-expr>: 的形式。

语句int i; 是变量的定义但没有初始化。因此,不会绕过变量的初始化。跳过这个定义没有比一开始更大的问题了。当然,在跳转到 case 1: 时会分配值,而不是在跳转到 case 2: 时分配值,但这与在 switch 语句之外的代码中发生的情况没有什么不同,如果人们只定义变量。

【讨论】:

  • @alestanis:当然,它可以编译:代码格式正确,只是在跳转到case 2: 标签时导致未定义的行为。不过,如果foo == 2,它应该会发出警告。刚刚发现为什么它不发出警告:它没有发出警告,因为变量刚刚定义但未初始化。初始化看起来像int i = 0;
【解决方案2】:

首先,编译器决定如何打开switch 到汇编代码。可能是if,也可能是goto。另外,如果您将变量初始化一起编写声明,编译器会告诉您这是error。在另一种情况下,它将是warning(编译器会警告您,但不会拒绝您)。所以,你可以保护自己。只是它调整编译器选项,warnings 将是errors。 要正确使用switch 中的变量,您必须指定它们的范围。例如:

switch(i)
{
case 1:
{
  int j = 0;
}
break;
}

PS。对于 c++,switch 是地狱。

来自 C++11:

C.1.5 第 6 条:声明

6.4.2、6.6.4(switch 和 goto 语句)

更改:现在跳过具有显式或隐式初始化程序的声明是无效的(除非跨整个块未进入)

基本原理:初始化器中使用的构造函数可能会分配在离开块时需要释放的资源。 允许跳过初始化器需要复杂的运行时 分配的确定。此外,任何使用未初始化的 对象可能是一场灾难。有了这个简单的编译时规则,C++ 确保如果一个初始化变量在范围内,那么它有 肯定已经初始化了。

对原始特征的影响:删除语义明确的特征。

来自 gcc 诊断编译指示:

6.57.1​​0 诊断编译指示 Diagnostic Pragmas

GCC 允许用户有选择地启用或禁用某些类型的 诊断,并更改诊断类型。例如,一个 项目的政策可能要求所有源代码都使用 -Werror 进行编译 但某些文件可能有例外,允许特定类型的 警告。或者,一个项目可能有选择地启用诊断和 将它们视为错误,具体取决于哪些预处理器宏 已定义。

PSS. 编译器知道在代码块中您的变量未初始化。不管是什么static C/C++ analysis(例如,免费cppcheck)都会告诉你问题的地方。

【讨论】:

  • 当然,你怎么看 Martin Fowler 和多态性?
  • 我认为将逻辑拆分为 20 个类,每个类都使用一个私有方法来避免使用一个 switch 语句,这将使我在他们被解雇后长时间修复他们的代码。
  • 我谈到了滥用开关。
【解决方案3】:

在第二种情况下打开警告(-Wall)给出:

foo.cpp:15:8: warning: 'i' may be used uninitialized in this function [-Wuninitialized]

跳过初始化是一个错误,但编译器不会试图在未初始化的变量上猜测你。

【讨论】:

    【解决方案4】:

    在 switch 内部初始化变量会导致此错误。这是由于权限问题而发生的。尝试在 switch 外部初始化变量并在 switch 内部赋值。

    同样的问题也适用于 C++ 线程。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多