代码:
#include<stdio.h>
#include<stdlib.h>
int arr[] = {1, 2, 3, 4};
static int count = 0;
int incr( ) {
++count;
++count;
return count;
}
int main(void)
{
printf("\ncount= %d \n",count);
int i;
arr[count++]=incr( );
for(i=0;i<4;i++)
printf("arr[%d]=%d\n", i,arr[i]);
printf("\nIncremented count= %d \n",count);
return 0;
}
输出
count = 0
arr[0]=2
arr[1]=2
arr[2]=3
arr[3]=4
Incremented count = 1
全局变量计数的最终递增值为1,即使它已经递增三次。
count++
在count
中被arr[count++]=incr( )
替换后,最终递增的count值为2。
答案 0 :(得分:7)
这是错误排序的未定义行为。在这一行:
arr[count++]=incr( );
使用您的编译器会发生什么:
你会发现更多关于&#34;副作用的信息&#34;和#34;序列点&#34;用谷歌搜索他们的真名:)
答案 1 :(得分:5)
要了解代码出错的原因,您必须首先了解undefined behavior and sequence points,这是一个相当高级的主题。您还需要了解未定义的行为是什么,以及未指定的行为是什么,explained here。
如果对作为副作用的变量执行某些操作(例如修改它),则不允许在下一个序列点之前再次访问该变量,除此之外的其他目的计算要存储在变量中的值。
例如i = i++
是未定义的行为,因为对同一个变量有两个副作用,中间没有序列点。但i = i+1;
定义明确,因为只有一个副作用(赋值),而i+1
只是一个读取权限,用于确定要存储的值。
在您的情况下,arr[count++]
子表达式和incr()
子表达式之间没有序列点,因此您会得到未定义的行为。
这就是序列点在函数中的出现方式,C11 6.5.2.2:
在评估函数后有一个序列点 指定者和实际参数但在实际调用之前。一切 调用函数中的求值(包括其他函数调用) 在此之前或之后没有特别排序的 被调用函数体的执行是不确定的 关于被调用函数的执行顺序。
这意味着函数的内容不会与表达式的其余部分相关。所以你基本上写的是一个与arr[count++] = ++count;
相同的表达式,除非你设法在操作的右侧挤压两个未经测序的++count
,否则这是不可能的。任何速率,都是未定义的行为。
通过在表达式的左手和右手之间强制执行序列点来修复代码。但是,子表达式的评估顺序是未指定的行为,因此无论是否首先评估左侧或右侧,您都需要确保代码是安全的。此代码将解决问题:
// artificial example, don't write code like this
0,arr[count++] = 0,incr();
因为逗号运算符引入了一个序列点。但是,当然,编写像这样的无意义代码并不是你应该做的事情。真正的解决方案是永远在同一表达式中与其他运算符一起使用++。
// good code, write code like this
arr[count] = incr();
count++;