C学习:一个例子搞清左值右值

概念理清


对所有的变量和表达式可归为两类,左值和右值。一句话直白讲:

  • 左值:非临时对象,多条语句中均可使用; 可出现在等号左右侧
  • 右值:临时对象,仅当前语句有效;只能出现在等号右侧

以上概念十分重要,务必理解并记忆。

一个例子


假设这样一个问题:

有个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;
}