使用 goto 跳转到内部或同级范围
Posted
技术标签:
【中文标题】使用 goto 跳转到内部或同级范围【英文标题】:Using goto to jump to inner or sibling scope 【发布时间】:2016-09-19 04:49:17 【问题描述】:是否允许跳转到内部作用域或同级作用域内的标签?如果是,是否允许使用在该范围内声明的变量?
考虑这段代码:
int cond(void);
void use(int);
void foo()
int y = 2;
label:
use(y);
int z = 3;
use(z);
/* jump to sibling scope: */ if(cond()) goto label;
/* jump to inner scope: */ if(cond()) goto label;
这些goto
s 合法吗?
如果是这样,当我跳转到 label
并保持分配给它的最后一个值 (2
) 时,y
是否保证存在?
或者编译器是否允许假设y
在超出范围后不会被使用,这意味着单个内存位置可以同时用于y
和z
?
如果此代码的行为未定义,我如何让 GCC 发出有关它的警告?
【问题讨论】:
你为什么还要使用goto
?
@EdHeal 我只是想了解 C。我实际上并没有使用此代码。
goto
's 对于打破(或进入)嵌套循环非常宝贵,否则考虑到break
的限制,这些循环将有助于更长的或递归的解决方案。在这种情况下,小老goto
是一个主力。 C 库经常使用goto
。看看qsort
源代码,如果我记得的话strlen
等等。
@DavidC.Rankin,你忘记了goto
用于清理代码的简化,正如 Linux 内核反复证明的那样。
【参考方案1】:
来自 C99 标准(重点是我的):
6.2.4 对象的存储时长
[6] 对于这种具有可变长度数组类型的对象,它的生命周期从对象的声明开始,直到程序的执行离开声明的范围。 ...如果递归输入范围,则每次都会创建一个新的对象实例。对象的初始值不确定。
6.8.6.1
goto
声明[1]
goto
语句中的标识符应命名位于封闭函数中某处的标签。goto
语句不应从具有可变修改类型的标识符范围之外跳转到该标识符范围内。[4] ...
goto
语句不允许跳过任何具有可变修改类型的对象声明。
结论
y
不是可变修改类型,因此,根据标准,跳转是合法。
y
保证存在,但是跳转会跳过初始化 (y = 2
),因此 y
的值是indeterminate。
您可以使用-Wjump-misses-init
让 GCC 发出 警告,如下所示:
warning:
jump skips variable initialization [-Wjump-misses-init]
在C++中,跳转是不合法的,C++不允许跳过y
的初始化。
【讨论】:
对于未来的读者:“可变修改类型”将包括例如像char buf[n]
这样的 VLA,或者类型包括可变大小的指针,如 char (*vla_ptr)[n]
(即 2D array pointer 与变量边界)。【参考方案2】:
跳转是合法的(在 C 中,在 C++ 中它们不是)。
当我跳转到
label
时,保证存在y
是的。
并保留分配给它的最后一个值 (
2
)?
没有。
来自C11 Standard (draft) 6.2.4/6:
对于这样的对象[没有存储类 说明符 static] 没有可变长度数组类型,它的生命周期延长 从进入与其关联的块直到该块的执行结束 反正。 [...] 对象的初始值是不确定的。如果 为对象指定了初始化,每次在执行块时达到声明 [...] 时都会执行它;否则,值变为 每次达到声明时都不确定。
从上面可以得出结论,第二次和第三次 use(y)
被称为 y
ins "indeterminate[d]" 的值,因为 y
的初始化是没有“到达”。
【讨论】:
以上是关于使用 goto 跳转到内部或同级范围的主要内容,如果未能解决你的问题,请参考以下文章
跳转语句 break;continue; return; goto 区别用法