C语言面试干货——C语言自增/自减操作的陷阱

Posted 从善若水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言面试干货——C语言自增/自减操作的陷阱相关的知识,希望对你有一定的参考价值。

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。

C语言面试干货——C语言自增/自减操作的陷阱

【陷阱1】自增/自减在表达式求值中的陷阱

“预测”一下这段code的输出

#include<stdio.h>

int main(void)
{
	int a,b=2;

	a= b*2 + 5*(1+b++);
	
	printf("a=%d b=%d\\n",a,b);
	
	return 0;
}

大家肯定告诉我答案是a=19,b=3;
真的是这样吗?看一下上面这段code在我电脑上的输出

b=3没有疑问,但是为什么a=21?

(你的IDE可能运行的结果是a=19,但这不能说明在其它IDE上也能得到正确的值)

再来看这段code

#include<stdio.h>

int main(void)
{
	int a,b=2;

	a = b++ +b++;
	
	printf("a=%d b=%d\\n",a,b);
	
	return 0;
}

大家肯定又告诉我答案是a=5,b=4;
真的是这样吗?看一下上面这段code在我电脑上的输出

其实上面两段Demo的结果在C语言中都是未定义的,原因在于C编译器的设计。
C编译器在设计时为了提升效率,会自行选择先对表达式中的哪个子表达式求值

例如在第一个Demo中的表达式 “a= b×2 + 5×(1+b++)”,编译器可能先对 “5×(1+b++)”进行求值,求值后b变成了3,然后再对“b×2”求值等于6,最后a=6+15=21;也有可能按照从左到右的顺序计算“a= b×2 + 5×(1+b++)”,这样得到的结果就是a=4+15=19

再看第二个Demo中的“a = b++ +b++”,我们自然会认为先计算左边的“b++”,再计算右边的“b++”,最后求和a=2+3=5;但是取决于编译器的设计,也可以先使用b的旧值2进行加法操作,然后对b自加两次,最后结果a=4

所以上面表达式的结果都是未定义的,取决于你使用的编译器,如果有面试官问你这样的题直接告诉他结果未定义


【陷阱2】在printf中使用自增/自减的陷阱

看下面这段code,告诉我结果是多少?

#include<stdio.h>

int main(void)
{
	int a=5;
	
	printf("a=%d a*a=%d\\n",a,a*a++);
	
	return 0;
}

同学们自信的告诉我答案是 a=5,a×a=25;
来看一下我电脑的输出:

原理同上编译器可以自行选择先对函数中的哪个参数求值

在这个例子中,编译器先计算右边的参数,并且在计算右边的参数时按照从右到左求值,先计算“a++”,再计算“a×a”(此时a×a中第一个a=6,第二个a=5),最后计算左边的参数a=6

其实在其它编译器下还有可能有其它的结果出现,这里不在赘述这些可能的值了(e.g. a=5 a×a=30)

【避免】上面的问题怎么避免?

  • 如果一个变量出现在一个函数的多个参数中,不要对该变量使用递增或递减运算符;
  • 如果一个变量多次出现在一个表达式中,不要对该变量使用递增或递减运算符。

自增/自减面试常问问题

问题1 :

#include<stdio.h>

int main(void)
{
	int a,b=5;
	
	a=b+++3;
	printf("a=%d b=%d\\n",a,b);
	
	return 0;
}

答案是: a=8 b=6
这里考察的是c语言编译器的“maximal munch strategy”特性,类似算法中的贪婪算法,编译器会将“a=b+++3;”识别为“a=b++ +3;

问题2:

#include<stdio.h>

int main(void)
{
	int a,b=5,c=6;
	
	//a=b+++++c;  //case 1:编译error 
	//a=b++ +++c; //case 2:编译error 
	//a=b+++ ++c; //case 3:编译success 
	a=b++ + ++c;//case 4:编译uccess 
	printf("a=%d b=%d c=%d\\n",a,b,c);
	
	return 0;
}

答案是:a=12 b=6 c=7
我们要解释的是为什么case1case2编译失败。

解释上面的问题,这几个问题必须先强调:

  • 我们要肯定C语言中空格的重要性,它不是可有可无的;
  • 然后我们要知道对一个右值和不可变左值进行自增操作在C语言中是不允许的。

我们上面提到了c语言编译器的“maximal munch strategy”特性,在这个特性下:

  • case1中的“a=b+++++c;”被解析为“a=b++++ +c;”,但我们知道“b++”返回的值是一个右值不能使用自增操作,所以编译error;
  • case2中的“a=b++ +++c;”被解析为“a=b++++ +c;”,所以编译error(如果你不相信可以将“a=b++ +++c;”改为“a=b++ +(++c);”即可编译通过),有同学疑问case3为什么编译通过了,这就是我们强调的空格的重要性“++”表示自增,而“+ +”就是两个加号。

That’s All

以上是关于C语言面试干货——C语言自增/自减操作的陷阱的主要内容,如果未能解决你的问题,请参考以下文章

在C语言中自增自减运算符有啥作用?

C语言后置自增啥时候自增(自减)?

c语言中条件判断表达式中出现自增自减符时的运算顺序

c语言中自增自减运算符的运算次序?

c语言中自增自减运算符的运算次序?

C语言 指针自增自减&加减运算 p++ p+i