注:本文是结合《C陷阱和缺陷》所写!
一.=和==
我们都知道在C语言中=表示赋值操作符,==表示比较,那么你知道为啥单等号为=,双等号为比较吗?
这里扩展下:因为在C语言中赋值操作符相对于比较符号较常出现,因此字符较少的符号=就被赋予了更常用的含义—赋值操作。
但是这样的便利却带来了一个非常常见错误,赋值操作符和比较运算错用!
看案例:
#include int main(){char c;scanf("%c",&c);while (c = ' ' || c == '\t' || c == '\n'){printf("hello\n");}return 0;}
你猜猜结果为啥呢?
结果为无限循环,原因就是因为将第一个==错写成了=,导致出现下述情况:
||优先级大于=优先级(逻辑运算符优先级大于赋值运算符优先级),所以该式子:c = ‘ ‘ || c == ‘\t’ || c == ‘\n’是将 ‘ ‘ || c == ‘\t’ || c == ‘\n’表达式结果赋值给c,然后判断是否为真,是真就继续下循环,因此出现无限循环的情况。
在为大家写一种两者区分写的特殊情况:
#include int main(){int x;int y;if (x = y){;}return 0;}//目的是将y的值赋值给x,并判断新值是否为0
如果我们完成上述目的直接这样写,可能你所在的编译器不会报错,但实际这种代码是非常不可取的,我们可以这样写:
#include int main(){int x;int y;if ((x=y)!=0){;}return 0;}
这样可以目的一目了然,也可以防止自己误判!
总结:
因此在写C语言代码用到=和==时,一定要问自己一句,到底要干什么!!!
二.词法分析之“贪心法”
我们知道C语言符号分为单字符符号和多字符符号,那么你知道计算机是如何判断连续的字符是哪种符号吗?比如:a+++++b如何区分呢?
C语言定义了一个符号规则:
每个符号应该包含尽可能多的字符!
简单理解就是编译器分解符号的方法是:从左向右一个字符一个字符的读入,如果该字符能够组成一个已存在的符号或一个符号的部分,就继续读取下一个字符,重复该操作直到读入的字符组成的字符串不再可能组成一个有意义的符号停止,该策略就为“贪心法”。
在这里补充一个知识:除字符串和字符常量,符号的中间不能镶嵌空白(空格符,制表符和换行符)
看案例:
#include int main(){int a = 3;int b = 3;int c = 0;c = a---b;printf("a---b=%d\n", c);a = 3; b = 3;c = a -- - b;printf("a -- - b=%d\n", c);a = 3; b = 3;c = a - -- b;printf("a - -- b=%d\n", c);}
你能得出三个printf的结果吗?(联系上面的贪心法规则)
结果如下:
解释:
我们可以将上面的代码看成这样:
#include int main(){int a = 3;int b = 3;int c = 0;//c = a---b;c=(a--)-b;printf("a---b=%d\n", c);a = 3; b = 3;//c = a -- - b;c=(a--)-b;//忽略空格printf("a -- - b=%d\n", c);a = 3; b = 3;//c = a - -- b;c=a-(--b);printf("a - -- b=%d\n", c);}
从而得到结果!
关于符号的准二义性还有这些:
y=x/*p;y=x/(*p);
本意是将x除以指针p指向的值,再把所得的商赋值给y,结果写成了第一行的代码,显然,问题大了,在编译器中看到/*就会被认为代码注释的开始,直到找到*/结束,所以我们因该写成第二行。
这里我们再回到开头这题:
a+++++b;//可以理解为((a++)++)+b;
三.对标准输入输出printf和scanf理解
我们直接看案例:
#include int main(){int i = 3;printf("%d,%d\n", i++, i++);}
结果为:
为啥呢?不应该是 3,4吗?
在这里我们必须知道标准输入输出规则:printf从右向左输出,scanf从左向右输入
因此我们是先算后面一个i++,在算前一个i++,结果为:4,3
学会了吗?
再看一个代码:
#include int main(){printf("%d", printf("%d", printf("43")));return 0;}
结果为:
这是因为printf也有返回值,返回值是输出的字符数~!
最后,希望大家C语言学习犯错越来越少,加油!!!