6.8.6 跳转语句
语法
1、jump-statement:
goto identifier ;
continue ;
break ;
return expressionopt ;
语义
2、一条跳转语句引发一个无条件跳转到另一个地方。
6.8.6.1 goto语句
约束
1、一条goto语句中的标识符应该命名一个标签,该标签坐落于当前封闭函数的某个地方。一条goto语句不应该从一个具有可变修改类型的标识符作用域的外部跳转到该标识符作用域的内部。
语义
2、一条goto语句引发一次无条件跳转到在封闭函数中由命名标签作为前缀的语句处。
3、例1 有时为了图方便而跳入到一组复杂语句集的中间。以下概述呈现了一个基于三个假定问题的可能解决方法:
(1)通用的初始化代码仅访问对当前函数可见的对象。
(2)通用初始化代码太大,以至于难以重复。
(3)要判定下一步操作的代码在循环的开头。(为了允许它来通过continue语句而到达,比如)
/* ... * goto first_time; for( ; ;) { // 判定下一步操作 /* ... */ if( 需要重新初始化) { // 仅仅重新初始化代码 /* ... */ first_time: // 通用初始化代码 /* ... */ continue; } // 处理其它操作 /* ... */ }
4、例2 一条goto语句并不允许跳过任何可变修改类型的对象声明。在作用域范围内的一次跳转是被允许的。
goto lab3; // 无效:跳转到VLA的作用域 { double a[n]; a[j] = 4.4; lab3: a[j] = 3.3'; goto lab4; // 有效:在VLA的作用域内跳转 lab4: a[j] = 6.6; } goto lab4; // 无效:跳转到VLA的作用域
6.8.6.2 continue语句
约束
1、一条continue语句应该仅出现在一个循环体内,或者作为一个循环体出现。
语义
2、一条continue语句使得一个跳转到封闭迭代语句的最小循环继续部分;也就是说,循环体的末尾。更精确地说,在每条语句中
while (/* ... */) { /* ... */ continue; /* ... */ contin: ; } do { /* ... */ continue; /* ... */ contin: ; } while (/* ... */); for (/* ... */) { /* ... */ continue; /* ... */ contin: ; }
上述代码中,除非continue语句在一条封闭的迭代语句内(在这种情况下,它在那条语句内解析),否则它就等价于goto contin;。[注:跟在contin:标签之后的是一条空语句。]
6.8.6.3 break语句
约束
1、一条break语句应该仅出现在一条switch体或循环体中,或作为一个switch体或循环体。
语义
2、一条break语句终结了最小封闭的switch或迭代语句的执行。
6.8.6.4 return语句
约束
1、一条带有一个表达式的return语句不应该出现在返回类型是void的一个函数中。一条不带表达式的return语句应该仅出现在返回类型是void的函数中。
语义
2、一条return语句终止当前函数的执行,并将控制返回给其调用者。一个函数可以具有任意数量的return语句。
3、如果一条return语句带有一个要被执行的表达式,表达式的值作为函数调用表达式的值返回给调用者。如果表达式具有一个不同于函数的返回类型的一个类型,那么该值被转换,就好比赋值给一个具有函数返回类型的一个对象。[注:return语句并不是一个赋值。子条款6.5.16.1的跌交限制并不应用于函数返回的情况。浮点值的表示可以具有比类型所指示的更宽广的精度范围;一个投射可以被用于移除此额外的范围和精度。]
4、例:在以下代码片段
struct s { double i; } f(void); union { struct { int f1; struct s f2; } u1; struct { struct s f3; int f4; } u2; } g; struct s f(void) { return g.u1.f2; } /* ... */ g.u2.f3 = f();
这里没有未定义行为,尽管如果直接做赋值可能会有(没有使用一个函数调用来获取该值)。