一个众所周知的危险错误是,函数返回了一个局部变量的指针或引用。一旦函数栈被销毁,这个指针就成为了野指针,导致未定义行为。而左值(lvalue)和右值(rvalue)的概念,本质上,是理解“程序员可以放心使用的变量”。

空泛的讨论先到这里,先看一段会报错的代码:

#include <iostream>

using std::cout;
using std::endl; int foo(int &a) {
return a;
} int main() {
int a = ;
cout << &a << endl;
int *p = &foo(a);
}

这里,对foo(a)取地址会引起错误: "lvalue required as left operand of assignment".字面理解是,&取地址运算符只能获取左值的地址。

毫无疑问的是,foo(a)的值是存在的,是数值1。之所以报错,是因为编译器认为它不是左值,不允许程序员获取它存放的地址。

我对这点标准的理解是:

获取一个临时空间的地址通常意味着要对这块内存赋值。在C++程序中,临时空间的销毁时机是不确定(undefined)的,它随时被用于其他临时空间的存储。于是程序员不允许使用这块空间。联系之前总结的堆栈概念,可以对C++中的变量存储有更深的理解。

堆栈概念一文,我小结了C++程序中数据可以存在三个地方:

1. 函数栈,在函数体内的定义的变量

2. 堆,特别指使用new,malloc获取的内存空间

3. 静态数据区,即.data 和.bss

对于lvalue的通俗描述,是“具有确定地址的非临时对象”,而不满足lvalue定义的值均被认为是rvalue。换句话说,C++程序里面出现的值,非左即右。下面我们分析一下这三个存放数据的区域里面可以被使用的值的情况:

堆的空间上的变量完全由程序员申请和管理的,所以它们都有明确的地址,是可以放心使用的左值。

静态数据区

对于静态数据区,尽管存放的位置是固定的,但里面的数据并不能认为都是左值。主要是因为里面有“字面值”,包括const所实现的常量,即静态存储而不能被修改的值。

函数栈

当函数调用发生的时候,系统会创建函数栈,保留上下文,函数调用结束的时候,函数栈内的变量会被销毁。函数体里面定义的变量是左值,而临时变量是右值。

拓展

C++ 11标准,为了更好地利用临时变量,提出Rvalue Reference,对应的的实现是move semantics (转移语意)和Perfect Forwarding(完美转发)。对这些新特性还不了解,暂时不写。

参考:

http://www.cnblogs.com/dejavu/archive/2012/09/02/2667640.html

http://stackoverflow.com/questions/230584/where-are-variables-in-c-stored

http://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c

理解C++ lvalue与rvalue的更多相关文章

  1. [C++] Lvalue and Rvalue Reference

    Lvalue and Rvalue Reference int a = 10;// a is in stack int& ra = a; // 左值引用 int* && pa ...

  2. 理解lvalue和rvalue

    今天看C++模板的资料,里面说到lvalue,rvalue的问题,这个问题以前也看到过,也查过相关资料,但是没有考虑得很深,只知道rvalue不能取地址,不能赋值等等一些规则.今天则突然有了更深层次的 ...

  3. C中的lvalue和rvalue

    该贴子第一条回答虽然浅尝辄止,但还是很有参考价值. https://www.quora.com/What-is-lvalue-and-rvalue-in-C IBM一个简单的说法是: "-通 ...

  4. L-value 和 R-value.

    An L-value is something that can appear on the left side of an equal sign, An R-value is something t ...

  5. 左值 lvalue,右值 rvalue 和 移动语义 std::move

    参考文章: [1] 基础篇:lvalue,rvalue和move [2] 深入浅出 C++ 右值引用 [3] Modern CPP Tutorial [4] 右值引用与转移语义 刷 Leetcode ...

  6. C++ lvalue(左值)和rvalue(右值)

    lvalue(左值)和rvalue(右值) 昨天写代码遇见一个这样的错误:{ "cannot bind non-const lvalue reference of type 'int& ...

  7. C++11中rvalue references的使用

    Rvalue references are a feature of C++ that was added with the C++11 standard. The syntax of an rval ...

  8. c++ 左值 和 右值

    什么是lvalue, 什么是rvalue? lvalue: 具有存储性质的对象,即lvalue对象,是指要实际占用内存空间.有内存地址的那些实体对象,例如:变量(variables).函数.函数指针等 ...

  9. 对象复制问题 && lvalue-rvalue && 引用

    按值传递实参到函数和函数返回临时变量的副本,函数的效率对执行性能来说至关重要 如果避免这样的复制操作,则执行时间可能会大大缩短. class CMessage { private: char * m_ ...

随机推荐

  1. QT Creator 快速入门教程 读书笔记(三)

    一   信号和槽 GUI 程序除了要绘制控件,还要响应系统和用户事件,例如重绘.绘制完成.点击鼠标.敲击键盘等.当事件发生时,UI 会产生相应的变化,让用户直观地看到. 大部分编程(例如Win SDK ...

  2. unity插件开发——一个例子:简单的svn集成

    在unity开发过程中,通常我们习惯性地在Windows操作系统下使用svn进行版本管理,而每次提交更新,都需要回到文件夹下的这种操作让人无法忍受.是不是可以集成svn到unity中呢?查了一圈uni ...

  3. API测试自动化——基于CDIF的SOA基本功能(实例篇)

    今天我们通过一些实例来体验一下API的自动化测试,感受一下基于CDIF的SOA的一些基本功能. 传统的测试工具在测试一个API的时候,必须手动填写这个API所需要接收的所有信息,比如一个查询航班动态的 ...

  4. Async/Await替代Promise的6个理由

    译者按: Node.js的异步编程方式有效提高了应用性能:然而回调地狱却让人望而生畏,Promise让我们告别回调函数,写出更优雅的异步代码:在实践过程中,却发现Promise并不完美:技术进步是无止 ...

  5. 老李秘技:loadrunner11.5支持net4.0么?

    老李秘技:loadrunner11.5支持net4.0么?   LoadRunner12.0以前的版本不支持.NET 4.0,已经证实R&D团队将在下一版本的LoadRunner即LoadRu ...

  6. Android开发艺术2之Activity的启动模式

    Activity是Android的四大组件之一,他的重要性毋庸置疑,对于这么重要的一个组件,我们首先要知道这些都是由系统进行管理和回调的,要理解Activity的启动模式,我们首先来了解一下Andro ...

  7. windows系统System32中各种实用的工具

    工具类 这些工具可以直接打开运行  输入名字就可以调出来了 我还会上传一个java程序,运行后会显示一个界面,直接调用这些工具 1.SnippingTool.exe   截图 2.calc.exe   ...

  8. C++中的继承详解(3)作用域与重定义,赋值兼容规则

    作用域与同名隐藏 一样的,先上代码 1 class A 2 { 3 public: 4 int a_data; 5 void a() 6 { 7 cout << "A" ...

  9. Apache2.4.23+PHP5.6.30+MySQL5.7.18安装教程

    最近在工作中常常接触到PHP,自己也写过一些简单的PHP页面.我们知道PHP是在服务器端运行的脚本语言,因此我们需要配置服务器环境.之前为了省事直接使用的是wamp集成环境,但是突然某一天领导要求我们 ...

  10. 【Tomcat源码学习】-3.应用管理

    通过上一节我们完成了对容器进行了加载.初始化.启动,而对于应用的加载部分独立出来,本节进行单独的讲解   一.应用加载流程 1)应用识别,Context创建 在Host启动后,会调用Host的监听Ho ...