lvalue和rvalue、传值和传引用、木桶

http://gideshi.blog.163.com/blog/static/8991803420081118115951620/

在ddj上看到一篇文章,针对初学者,不过我觉得还是有一定的难度的。本来想翻译过来,想想工作量会非常大,干脆结合自己的知识,总结几句。有兴趣的话可以参考原文“Computer Programming and Precise Terminology”。文中阐述了定义和声明的区别,写得很精彩,让大家了解了一些internals。由于我写过一篇相关的文章,因此这里不再啰嗦,想看的话可以参考“[原]C/C++:如何理解复杂的声明”。

lvalue和rvalue
在计算机的远古时代,变量的lvalue和rvalue是指:
lvalue:变量在内存中的位置。通过它能够找到内存中存放的变量(location value);
rvalue:存放在lvalue对应的内存中的东西(register value);

变量的lvalue的存在表明有存储空间,然而其rvalue并没有对应存储空间,它只是那片存储空间对应的值。如果不考虑类型的话,那个就是一个1和0的序列。

好了,我们来看看:

// Allocate 4 bytes on stack and set content to 10.
int i = 10;

例1

i的lvalue和rvalue如下(假定i在内存中的地址为0x1000):
   lvalue        rvalue
   --------      -------
   |0x1000|      | 10  |
   --------      -------
-〉|字长    |<-  ->|int  |<-

图1 i的lvalue和rvalue

对于:

// Allocate a machine word on stack and set content to NULL.
int *= NULL;

// Allocate 4 bytes on heap and set content to 10, then set p's rvalue to the start address of these bytes.
= new int(10);

例2

p的lvalue、rvalue以及真正的值如下(假定p的地址为0x1008):
  lvalue        rvalue
  --------      --------      -------
  |0x1008|      |0x5000|      | 10  |
  --------      --------      -------
-〉|字长    |<-  ->| 字长   |<-  ->|int  |<-

图2 p的lvalue和rvalue

  lvalue        rvalue
  --------      -------
  |0x5000|      | 10  |
  --------      -------
->| 字长   |<-  ->|int  |<-
图3 *p的lvalue和rvalue


传值和传引用
一个引用变量只能取两种值的rvalue:NULL和内存地址。在传值时传递的是rvalue,传引用则传递lvalue。也就是说:

int foo(int i);

int main()
{
    int i = 10;
    return foo(i);
}


这段代码中,foo()采用传值方式来传递参数。也就是说i的lvalue并没有被传递到foo()中。相反,在foo()的栈上会生成一个隐藏变量,并将i的rvalue,10,拷贝到其中——这是因为,如果没有该变量的lvalue,那么i的rvalue就没有存放的地方。这样,无论在foo()中对这个变量做任何修改都不会影响到i。如果要将foo()中i的修改反映到main()中,那就要采用传引用方式:

int foo(int *i);

int main()
{
    int i = 10;
    return foo(&i);
}


此时,&i表示的是传递i的lvalue。因此就不需要新建一个变量来存放该i的rvalue,相当于foo()直接访问main()中的i变量。这样就能够通过foo()来修改i的rvalue(参考图1)。

那么,如果我要修改例2中p的rvalue——使指针指向另一块内存——也是同样的道理:

int foo(int & *i);
// Or: int foo(int **i);
// But in c++ we prefer the first one.

int main()
{
    int *= NULL;
    p = new int(10);
    return foo(p);

    delete p;
}


此时根据foo()的signature,p的引用被传递。也就是说,p的lvalue被传递进去,那么修改p的rvalue时就能使p指向另一块内存(参考图2)。

不光有些变量同时具有lvalue和rvalue,表达式也可能同时具有lvalue和rvalue。在传递参数时需要显式指定使用lvalue或rvalue,除此之外,要判断使用的是表达式的lvalue还是rvalue,需要根据context来判定。参考“数组类型、函数类型到左值和右值的转换”,在这里有些争论,不过现在完全同意这篇文章关于l/rvalue的观点了。

木桶
对于表达式的lvalue来说,它的size是固定的——机器字长。而对于其rvalue来说就不是了。因此不能把两个不同类型的变量相互赋值。一定要这样做的话就需要使用类型转换。这就好比有很多木桶,每个木桶的编号长度都是固定的,但大小不一样。如果将小木桶里的水倒入大木桶中自然没有问题,但要反过来就会有问题,可能会导致信息丢失。

Copyleft (C) 2007, 2008 raof01. 本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系([email protected]),否则作者将使用法律来保证权利。

你可能感兴趣的:(value)