请解释以下C代码的输出

时间:2014-06-02 09:11:09

标签: c operator-precedence

代码:

#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。

2 个答案:

答案 0 :(得分:7)

这是错误排序的未定义行为。在这一行:

arr[count++]=incr( );

使用您的编译器会发生什么:

  • arr [count]被解析为arr [0],postfix ++将被应用到最后 语句;
  • 调用
  • incr(),count现在等于2,incr()返回2;
  • arr [0]被分配2;
  • postfix ++的副作用开始了,而count现在等于1.以前的计数更改都会丢失。

你会发现更多关于&#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++;