分三部分来讲
一、左值与右值
参看:左值与右值
首先我们需要理解左值和右值的定义:
左值指的是如果一个表达式可以引用到某一个对象,并且这个对象是一块内存空间且可以被检查和存储,那么这个表达式就可以做为一个左值。
右值指的是引用了一个存储在某个内存地址里的数据。
从上面的两个定义可以看出,左值其实要引用一个对象,而一个对象在我们的程序中又肯定有一个名字或者可以通过一个名字访问到,所以左值又可以归纳为:左值表示程序中必须有一个特定的名字引用到这个值。而右值引用的是地址里的内容,所以相反右值又可以归纳为:右值表示程序中没有一个特定的名字引用到这个值除了用地址。
好这些都是从定义上理解左值右值,那么我们再用这些定义作为我们的理论基础来总结一下哪些是左值,哪些是右值:
左值:
右值:
以上这些内容都可以用定义来解释为什么这些为左值,而那些为右值。但我要特殊解释一下为什么函数的调用只能作为右值除了这个函数返回的是引用。其实这个也非常好解释,因为如果一个函数返回的值是内建类型,那么这个返回值是没有办法通过一个名字或者表达式引用到的,同理如果一个函数返回的是一个对象,那么这个对象是一个临时的,也不可能用一个名字访问到。所以函数的调用通常只能作为右值,但如果一个函数返回引用,那么它的返回值就有意义了,因为它是另一个名字的别名,有名字了,所以它就变成了左值。
注意:左值能转化为右值,但反之不行。
好了,讲了这么多我觉得已经足够,但还要多讲一点,这点就是哪些操作符必需左值.
附:《C primer plus》中左值、右值的定义
“数据对象”是泛指数据存储区的术语,数据存储区能用于保存值。
“左值”指用于标识一个特定的数据对象的名字或表达式。即对象指实际的数据存储,而左值是用于识别或定位那个存储的标识符(或表达式)。
“右值”指可能赋给可修改左值的量。
二、运算符优先级
参看:C语言运算符优先级列表(超详细)
说明: 同一优先级的运算符,运算次序由结合方向所决定。 简单记就是:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
一些容易出错的优先级问题
三、扩展总结
1、赋值运算符:=
赋值运算符的动作是从右到左,且不能将一个值赋给一个常量。赋值,从C的角度来看,主要目的是对表达式来求值。
2、除法运算符:/
7/4 = 1;
7./4 = 1.75;
7./4. = 1.75;
11 / 5 = 2; 11 / -5 = -2; -11 / -5 = 2; -11 / 5 = -2;
在C中,整数除法结果的小数部分都被丢弃,没有把整数除法运算的结果四舍五入到最近的整数。
当对整数与浮点数进行混合运算时,结果是浮点数。
3、取余运算符:%
取余运算符用于整数运算,该运算符计算出用它右边的整数去除它左边的整数得到的余数。
注意:不要对浮点数使用该运算符,那将是无效的。
负数取余规则:
如果第一个操作数为负数,那么得到的余数也为负数;如果第一个操作数为正数,那么得到的余数也为正数。
11 % 5 = 1;11 % -5 = 1;-11 % 5 = -1;-11 % -5 = -1;
4、自增和自减:++和--
后缀: a++; a--; 使用a的值之后改变a;
前缀: ++a; --a; 使用a的值之前改变a;
++a = 10;
++a 的结果是a值的拷贝,并不是变量本身,你无法向一个值进行赋值。赋值运算的左操作数必须是左值。
后续补充几个例题。。。
四、布尔值
通过包含stdbool.h头文件,可以用bool代替关键字_BOOL表示这种类型,并用标识符true和false代替1和0.
五、逻辑运算符
与(&&)、或(||)、非(!)
或(||)和与(&&)都具有短路特征,如果前一个逻辑表达式的结果可以决定整个表达式结果则计算机会忽略后面的逻辑表达式。
短路特性具体为:
表达式一 && 表达式二 如果表达式一为假,就不会进行表达式二的计算
表达式一 || 表达式二 如果表达式一为真,就不会进行表达式二的计算
六、三目表达式
三目操作符可以在两个不同的计算机规则中选择一个
三目操作符的格式如下
布尔值 ? 公式一: 公式二
//布尔值需要有定义的 ret=(num >=0) ? num : 0-num;
如果布尔值为真则采用公式一计算结果
如果布尔值为假则采用公式二计算结果
七、== 和 =不同
符号 = 作为赋值运算,符号 ==作为比较。注意,用于测试两个表达式是否相等的操作符是==,如果无用了=操作符,虽然它也是合法的表达式,但其结果几乎肯定和你的本意不一样,它将执行赋值操作而不是比较操作。
比如下例,该语句本意似乎是要检查 x 是否等于 y
if (x = y)break;
而实际上是将 y 的值赋给了 x,然后检查该值是否为零。
还需注意:任何非零值都是真.比如,while (-1) == while (true)
再看下面的例子:
while (c = ' ' || c == 't' || c =='\n')c = getc (f);
本例中循环语句的本意是跳过文件中的空格符、制表符和换行符。由于程序员在比较字符 ' ' 和变量 c 时,误将比较运算符 == 写成了赋值运算符 = 。因为赋值运算符 = 的优先级要低于逻辑运算符 || ,因此实际上是将以下表达式的值赋给了 c
' ' || c == 't' || c == '\n'
因为 ' ' 不等于零 (' ' 的ASCII 码为 32),那么无论变量 c 此前为何值,上述表达式求值的结果是 1,因此循环将一直进行下去直到整个文件结束。
另一方面,如果把赋值运算符写成比较运算符,同样会造成混淆:
if ((filedesc == open(argv[i] , 0)) < 0)error ();
在本例中,如果函数open执行成功,将返回 0 或者正数;而如果函数 open 执行失败,将返回 -1.上面这段代码的本意是将函数 open的返回值存储在变量 filedesc 之中,然后通过比较变量 filedesc 是否小于 0 来检查函数 open 是否执行成功。但是,此处的 == 本应是 = 。而按照上面代码中的写法,实际进行的操作是比较函数 open 的返回值与变量 filedesc ,然后检查比较的结果是否小于 0.因此比价运算符 == 的结果只可能是 0 或 1.永远不可能小于 0,所以函数 error ( )将没有机会被调用。如果代码执行,似乎一些正常,除了变量 filedesc 的值不再是函数 open 的返回值。
八、在表达式中使用无符号数
库函数 strlen 的原型如下:
size_t strlen (char const *string);
注意:strlen 返回一个类型为 size_t 的值。这个类型是在头文件 stddef.h 中定义的,它是一个无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。例如下面的表达式:
#include <stdio.h>#include <string.h>int main (void){char ptr1[] = "beijing";char ptr2[] = "hello world";if (strlen (ptr1) - strlen (ptr2) >= 0){printf ("1111111111\n");}printf ("2222222222\n");return 0;}
strlen (ptr1) - strlen (ptr2) 为无符号类型,得不到想要的结果,应该为:
#include <stdio.h>#include <string.h>int main (void){char ptr1[] = "beijing";char ptr2[] = "hello world";if (strlen (ptr1) >= strlen (ptr2)){printf ("1111111111\n");}printf ("2222222222\n");return 0;}