把选择语句和重复语句视为块
问:在C99
中,为什么把选择语句和重复语句(以及它们的“内部”语句)视为块?
答:这条奇怪的规则来源于把复合字面量用于选择语句和重复语句时出现的一个问题。该问题与复合字面量的存储期限有关,所以我们先花点时间讨论一下这个问题。
C99
标准指出,如果复合字面量出现在函数体之外,那么复合字面量所表示的对象具有静态存储期限。否则,它具有自动存储期限,因而对象所占有的内存会在复合字面量所在块的末尾释放。
考虑下面的函数,该函数返回使用复合字面量创建的point
结构:
struct point create_point(int x, int y)
{
return (struct point) {x, y};
}
这个函数可以正确工作,因为复合字面量创建的对象会在函数返回时被复制。原始的对象将不复存在,但副本会保留。现在假设我们对函数进行微小的改动:
struct point *create_point(int x, int y)
{
return &(struct point) {x, y};
}
这一版本的create_point
函数会导致未定义的行为,因为它返回的指针所指向的对象具有自动存储期限,函数返回后该对象不复存在。
现在回到开始时得到的问题:为什么把选择语句和重复语句视为块?考虑下面的例子:
/* Example 1 - if statement without braces */
double *coefficients, value;
if (polynomial_selected == 1)
coefficients = (double[3]) {1.5, -3.0, 6.0};
else
coefficients = (double[3]) {4.5, 1.0, -3.5};
value = evaluate_polynomial(coefficients);
这个程序片段显然能按需要的方式工作。coefficients
将指向由复合字面量创建的两个对象之一,并且该对象在调用evaluate_polynomial
时仍然存在。现在考虑一下,如果在内部语句(if
语句控制的语句)两边加上花括号,会有什么不同:
/* Example 2 - if statement with braces */
double *coefficients, value;
if (polynomial_selected == 1) {
coefficients = (double[3]) {1.5, -3.0, 6.0};
} else {
coefficients = (double[3]) {4.5, 1.0, -3.5};
}
value = evaluate_polynomial(coefficients);
现在我们遇到问题了。每个复合字面量会创建一个对象,但是该对象只存在于包含相应语句的花括号所形成的块内。调用evaluate_polynomial
时,coefficients
指向一个不存在的对象,从而导到致未定义的行为。
C99
的创立者对这种现象很不满意,因为程序员不可能期望在if
语句中简单地增加花括号就会导致未定义的行为。为了避免这一问题,他们决定始终把内部语句视为一块。这样一来,示例1和示例2就等价了,都会导致未定义的行为。
当复合字面量是选择语句或重复语句的控制表达式的一部分时,类似的问题也会发生。因此,我们把整个选择语句和重复语句也都看作块(就好像有一对不可见的花括号在整个语句外面一样)。因此,带有else
子句的if
语句包含三个块:两个内部语句分别是一个块,整个if
语句又是一个块。