
2.3 拾遗

视频讲解
2.3.1 赋值运算符
赋值运算符是C语言中用得最多的一个运算符,因此它的设计简便与否直接影响到C语言的开发效率。语法很简单,就是将右边的值放到左边的变量里边,因为它的执行方向是自右向左:

需要注意的是,赋值运算符的左边必须是一个lvalue,变量名就是lvalue,但常数就不是了,所以把5写在赋值号的左边就会出错:

编译系统会提示类似于“error: lvalue required as left operand of assignment”的错误。很多读者会想当然地将lvalue“脑补”为“left value of the赋值运算符”,其实这样理解不完全正确。
【扩展阅读】关于什么是lvalue和rvalue,感兴趣的读者可以访问http://bbs.fishc.com/thread-69833-1-1.html或扫描图2-15的二维码进行查阅。

图2-15 什么是lvalue和rvalue
2.3.2 复合的赋值运算符
由于赋值运算符的使用频率非常高,所以C语言的发明者也想出了一些可以“偷懒”的方案。比如:

这样写太费劲了,完全可以写成:

同理还有:

2.3.3 自增自减运算符
当需要对一个变量加1或减1并赋值给自己的时候,可以写成i++、i--或++i、--i的形式。它们也被称为自增自减运算符,或加加减减运算符。
自增自减运算符可以放在变量之前,也可以放在变量之后。它们有什么区别呢?粗略地看,++i和i++都是实现将变量i的值加1并赋值给本身,也就相当于i=i+1。
但是将++i和i++赋值给一个变量的时候,两者的差异就产生了:

程序执行后,可以看得出来,两者的结果是不同的:

它们的区别是:i++是先使用变量i中保存的值,再对自身进行++运算;而++i则是先对自身进行++运算,再使用变量i的值(这时变量i的值已经加1了)。
另外,自增自减运算符只能作用于变量,而不能作用于常量或表达式。
2.3.4 逗号运算符
我们是在讲解for语句的时候接触过逗号运算符,for语句的表达式1和表达式3,可以通过使用逗号运算符来初始化多个值或者调整多个循环变量。
逗号运算符也可以单独使用,如对多个变量进行初始化时需要写很多行代码:

有了逗号运算符,可以将它们组合成一行,称为逗号表达式:

逗号表达式的语法为:

逗号表达式的运算过程为:从左往右逐个计算表达式。逗号表达式作为一个整体,它的值为最后一个表达式(也即表达式n)的值。不过,逗号运算符在C语言的所有运算符中,是“地位”最低的,因为连赋值运算符的优先级都比逗号运算符要高,所以:

相当于

如果要让变量a赋值为5,应该这样写:

再分析一个:

先将变量b赋值为3,然后变量c赋值为b+4的和,也就是7,接下来把c的值加上5,赋值给变量a,得到变量a的值是12。
逗号运算符的优先级是最低的,虽然c=b+4用优先级最高的小括号运算符包含起来,但只要在逗号表达式中,都应该从左到右依次执行每个表达式:

最后需要注意的是:在C语言中看到逗号,不一定就是逗号运算符,因为在有些地方,逗号仅仅是被用作分隔符而已。比如:
·int a, b, c;
·scanf("%d%d%d", &a, &b, &c);
这里逗号都是作为分隔符使用,而不是运算符。
2.3.5 条件运算符
有一个操作数的运算符称为单目运算符,有两个操作数的运算符称为双目运算符,C语言还有唯一的一个有三个操作数的三目运算符,它的作用是提供一种简写的方式来表示if-else语句。
这个三目运算符的语法格式为:

exp1是条件表达式,如果结果为真,返回exp2;如果为假,返回exp3。所以:

可以直接写成:

2.3.6 goto语句
goto语句可以说是一个历史遗留问题,因为早期的编程语言都留有很多汇编语言的痕迹,goto语句就是其中之一。goto语句的作用就是直接跳转到指定标签的位置,语法格式为:

其中标签需要被定位于某个语句的前边,比如:

程序实现如下:

很多初学者刚接触goto语句可能会觉得很实用,但是这里要强调的一点是:在开发中要尽量避免使用goto语句,因为随意使用goto语句在代码间跳来跳去,会破坏代码原有的逻辑。
但在一种情况下使用goto语句比较合适,那就是当需要跳出多层循环的时候,使用goto语句要比多个break语句好使。
2.3.7 注释
当工作了一段时间之后,你就会发现,代码的注释其实比代码本身更重要。有些读者可能会说:“注释有什么难的?不就是两个斜杠(//),并在后边说明这句代码是干什么用的吗?”非也非也!我曾经要求一位学生给下面这段代码写注释:

结果他这样写:

他把每句代码的作用都给写上去了,程序员应该都知道每行代码是干什么的。如果他不懂编程,看了你的注释也不可能就懂编程了。所以这样的注释写了等于没写,浪费时间。
那么合理的注释应该怎么写呢?
其实每个人对注释的理解都不一样,因为注释与其说是写给别人看的,不加说是写给以后自己看的。每个程序员随着时间的推移,都会累积一个自己的源代码“小金库”。说它是小金库还真不夸张,因为不可能每遇到一个新的问题就重新写一遍代码,这样工作效率就太低了。
现在写代码要尽可能考虑到以后会再次使用它,对于一些特殊的变量,应该注明它的作用和使用范围。对于程序或函数,应该在最前边写清楚它的功能、参数和返回值等。
C语言的注释有两种方式,一种是大家常用的,将注释写在两个连续斜杠的后边:

这样每一行注释的开头都需要有两个斜杠。
有时可能需要写多行注释,那么可以使用“/*注释的内容*/”来实现:
