概念理清
对所有的变量和表达式可归为两类,左值和右值。一句话直白讲:
左值
:非临时对象,多条语句中均可使用; 可出现在等号左右侧右值
:临时对象,仅当前语句有效;只能出现在等号右侧
以上概念十分重要,务必理解并记忆。
一个例子
假设这样一个问题:
有个
char *
类型的指针p,指向了一个int类型的首地址。我们希望跳过当前这个int
数据,指向下一个char
,思考应该如何操作?
进一步地,给出三种实现,判断是否正确:
// example1
p += sizeof(int);
// example2
((int *)p)++;
// example3
p = (char *)((int *)p + 1)
首先给出结论,第二个例子实现错误,其余两个正确。然后根据之前的左值右值概念知识,分析原因:
- 第一个例子,将
char *
类型的p往后移动int
所占空间大小的byte,可以达到效果。 - 第二个例子,自增和自减运算符
++
和--
只能针对左值操作,而(int *)p
做类型转换后,结果是右值,是临时对象,故无法进行自增。 - 第三个例子,将为右值的临时变量
(int *)p
加1后,再转换类型,结果也是右值,赋值给左值p,从而记录移位一个int内存空间的操作。
右值是表达式结束时就不再存在的临时对象,就像局部变量等函数调用结束就失效一样。所以可以小结出两个右值的重要判据(满足其一即可):
1)临时对象是右值
2)不能出现在等号左侧的是右值
更多右值举例(常量和算术表达式):
-
a << 2
移位运算后,结果是临时变量,是右值 -
a + 2 / 3
,结果是临时变量,是右值 -
a + b
,算术运算结果为临时变量,是右值 -
(char *)p
, 转换类型后,结果是临时变量,是右值 -
&arr[5]
,对数组第二个对象取地址,是算术运算得到的,是临时对象,是右值
测试代码
int main()
{
char *p;
char ch[5] = {
'1', '2','3','4','5'};
int *pInt = (int *)&ch[0];
// 正确,输出结果为5
p = (char *)pInt;
p += sizeof(int);
printf("%c", *p);
// 正确,输出结果为5
p = (char *)pInt;
p = (char *)((int *)p + 1);
printf("%c", *p);
// p = (char *)pInt;
// ((int *)p)++; // 该语句编译不通过
// (int *)p++; // 输出为2,不符合预期,先p++,再(int *)转换类型成临时变量,之后调用变量p时,类型还是char *
// printf("%c", *p);
while (1) {
;}
return 0;
}