理解 total += *start++;
C语言的专业术语副作用(side effect)。副作用是对数据对象或文件的修改。例如:states = 50;,它的副作用是将变量states的值设置为50。副作用?这似乎更像是主要目的!但是从C语言的角度来看,主要的目的是对表达式求值。给出表达式4 + 6,C会对其求值得10,给出表达式states = 50,C会对其求值为50。对该表达式的副作用是把变量states的值改为50。跟赋值运算符(=)一样,递增运算符(++)和递减运算(--)也有运算符,使用它们的目的主要就是使用其副作用。 类似的,调用printf()函数时,它显示的信息其实就是副作用(printf()的返回值是待显示字符的个数)。
/* C语言的副作用 p104 */ #include <stdio.h> int main(void) { int states; printf("%d \n", printf("%d %d\n", (states = 50), states)); return 0; }该程序运行后输出的结果为: 50 50 6
序列点(sequence point) 是程序执行的点,在该点上,所有的副作用都在进入下一步之前发生。在C语言中,语句中的分号标记了一个序列点。意思是,在一个语句中,赋值运算符、递增运算符和递减运算符对运算对象做的改变必须在程序执行下一条语句之前完成。另外,任何一个完整表达式的结束也是一个序列点。 什么是完整表达式?所谓完整表达式(full expression),就是指这个表达式不是另一个更大表达式的子表达式。例如,表达式语句中的表达式和while循环中的作为测试条件的表达式,都是完整表达式。 序列点有助于分析后缀递增何时发生。例如,考虑下面的代码:
while (guests++ < 10) printf("%d \n", guests);对于该例,初学者认为“先使用值,再递增它”的意思是,在printf()语句中先使用guests,再递增它。但是,表达式guests++ < 10是一个完整的表达式,因为它是while循环的测试条件,所以该表达式的结束就是一个序列点。因此,C保证了在程序转至执行printf()之前发生副作用(即,递增guests)。同时,使用后缀形式保证了guests在完成与10的比较后才进行递增。 现在,考虑下面这条语句:
y = (4 + x++) + (6 + x++);表达式4 + x++不是一个完整的表达式,所以C无法保证x在子表达式4 + x++求值后立即递增x。这里,完整表达式是整个赋值表达式语句,分号标记了序列点。所以,C保证程序在执行下一条语句之前递增x两次。C并未指明是在对子表达式求值以后递增x,还是对所有表达式求值后再递增x。因此要尽量必变编写类似的语句。
最后再来说一下为什么以下两个代码块是等价的:
while (start < end) { total += *start; //把数组元素的值加起来 start++; //让指针指向下一元素 } while (start < end) { total += *start++; //把数组元素的值加起来,让指针指向下一元素 }一元运算符*和++优先级相同,但结合律是从右往左,所以start++先求值,然后才是*start。也就是说,指针start先递增后指向。使用后缀形式(即start++而不是++start)意味着先把指针指向位置上的值加到total上,然后再递增指针(即,执行完整个表达式的其他操作后,再递增)。如果使用*++start,顺序则反过来,先递增指针,再使用指针指向位置上的值。(即,先递增,再执行表达式的其他操作)。如果使用(*start)++,则先使用start指向的值,再递增该值,而不是递增指针。这样,指针将一直指向同一个位置,但是该位置上的值发生了变化。虽然*start++的写法比较常用,但是*(start++)这样写更请楚。 下面的程序演示了这些优先级的情况:
/* 指针运算中的优先级 */ #include <stdio.h> int data[2] = {100, 200}; int moredata[2] = {300, 400}; int main(void) { int * p1, * p2, * p3; p1 = p2 = data; p3 = moredata; printf(" *p1 = %d, *p2 = %d, *p3 = %d\n", *p1 , *p2 , *p3); printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %d\n", *p1++ , *++p2 , (*p3)++); printf(" *p1 = %d, *p2 = %d, *p3 = %d\n", *p1 , *p2 , *p3); return 0; }该程序的输出:
*p1 = 100, *p2 = 100, *p3 = 300 *p1++ = 100, *++p2 = 200, (*p3)++ = 300 *p1 = 200, *p2 = 200, *p3 = 301只有(*p3)++改变了数组元素的值,其他两个操作分别把p1和p2指向数组的下一个元素。
