我已阅读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循环中,那么每次输入和离开范围时都不会调用构造函数和析构函数,从而导致效率降低?或者编译器会优化它吗?
答案 0 :(得分:6)
跳过对象的初始化,即使对象的类型为int
,也始终是未定义的行为。请注意,switch
- 语句的声明并不特别:它只是一个声明,人们[ab-]使用这种有趣的方式,例如Duff's Device。声明中唯一特别的标签是标签可以采用default:
和case <const-integer-expr>:
的形式。
语句int i;
是变量的定义,但没有初始化。因此,不绕过变量的初始化。跳过这个定义没有比首先出现的更大的问题。当然,当跳转到case 1:
而不是跳到case 2:
时,会分配值,但这与switch
之外的代码中发生的情况没有什么不同 - 如果人们只定义变量,则会发生声明。
答案 1 :(得分:2)
首先,编译器决定如何打开switch
汇编代码。可以是if
,也可以是goto
。另外,如果你将变量初始化一起写入声明,编译器会告诉你这是error
。在另一种情况下,它将是warning
(编译器警告你,但不会拒绝你)。所以,你可以保护自己。只是它调整编译器选项,其中warnings
将是errors
。
要正确使用switch
中的变量,您必须指定其范围。例如:
switch(i)
{
case 1:
{
int j = 0;
}
break;
}
PS。对于c ++,切换是地狱。
来自C ++ 11:
C.1.5第6条:陈述
6.4.2,6.6.4(switch和goto语句)
更改:现在使用显式或隐式初始化程序跳过声明现在无效(除了未输入的整个块之外)
原理:初始化程序中使用的构造函数可能会分配在离开块时需要取消分配的资源。 允许跳过初始化器需要复杂的运行时间 确定分配。此外,任何未初始化的使用 对象可能是一场灾难。有了这个简单的编译时规则,C ++ 确保如果初始化变量在范围内,那么它具有 确实已经初始化了。
对原始功能的影响:删除语义定义明确的功能。
来自gcc诊断编译指示:
6.57.10诊断编排 Diagnostic Pragmas
GCC允许用户有选择地启用或禁用某些类型的 诊断,并更改诊断的类型。例如,a 项目的策略可能要求所有源使用-Werror进行编译 但某些文件可能有例外,允许特定类型的 警告。或者,项目可以选择性地启用诊断和 根据预处理器宏的不同,将它们视为错误 定义
PSS。编译器知道你的变量未初始化的代码块。无论是什么static C/C++ analysis
(例如,免费cppcheck
)都显示问题所在。
答案 2 :(得分:1)
在第二种情况下打开警告(-Wall)给出:
foo.cpp:15:8: warning: 'i' may be used uninitialized in this function [-Wuninitialized]
跳过初始化是一个错误,但编译器不会尝试在未初始化的变量上进行分析。
答案 3 :(得分:0)
初始化开关内部的变量会导致此错误。发生这种情况是由于权限问题。尝试在开关外部初始化变量,然后在开关内部分配值。
同一问题适用于C ++线程。