lvalue和rvalue、传值和传引用、木桶
摘自:http://www.cnblogs.com/linyawen/archive/2011/12/07/2279936.html
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的序列。
好了,我们来看看:
|
i的lvalue和rvalue如下(假定i在内存中的地址为0x1000):
lvalue rvalue
-------- -------
|0x1000| | 10 |
-------- -------
-〉|字长 |<- ->|int |<-
对于:
|
p的lvalue、rvalue以及真正的值如下(假定p的地址为0x1008):
lvalue rvalue
-------- -------- -------
|0x1008| |0x5000| | 10 |
-------- -------- -------
-〉|字长 |<- ->| 字长 |<- ->|int |<-
lvalue rvalue
-------- -------
|0x5000| | 10 |
-------- -------
->| 字长 |<- ->|int |<-
传值和传引用
一个引用变量只能有取两种值的rvalue:NULL和内存地址。在传值时传递的是rvalue,传引用则传递lvalue。也就是说:
|
这段代码中,foo()采用传值方式来传递参数。也就是说i的lvalue并没有被传递到foo()中。相反,在foo()的栈上会生成一个隐藏变量,并将i的rvalue,10,拷贝到其中——这是因为,如果没有该变量的lvalue,那么i的rvalue就没有存放的地方。这样,无论在foo()中对这个变量做任何修改都不会影响到i。如果要将foo()中i的修改反映到main()中,那就要采用传引用方式:
|
此时,&i表示的是传递i的lvalue。因此就不需要新建一个变量来存放该i的rvalue,相当于foo()直接访问main()中的i变量。这样就能够通过foo()来修改i的rvalue(参考图1)。
那么,如果我要修改例2中p的rvalue——使指针指向另一块内存——也是同样的道理:
|
此时根据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. 本文可以用于除商业外的所有用途。此处“用途”包括(但不限于)拷贝/翻译(部分或全部),不包括根据本文描述来产生代码及思想。若用于非商业,请保留此权利声明,并标明文章原始地址和作者信息;若要用于商业,请与作者联系(raof01@gmail.com),否则作者将使用法律来保证权利。