Chinaunix首页 | 论坛 | 博客
  • 博客访问: 454766
  • 博文数量: 120
  • 博客积分: 2284
  • 博客等级: 大尉
  • 技术积分: 1330
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-25 10:49
个人简介

http://guliqun1983.blog.163.com/blog/static/501116852011730535314/

文章分类
文章存档

2013年(23)

2012年(23)

2011年(74)

分类: C/C++

2011-04-02 10:57:41

发现很多朋友对"lvalue""rvalue"理解有误,我先谈谈自己对此的一些理解,并期望能够引起更多朋友的广泛讨论。也算起到抛砖引玉的作用吧。
引用:注:

这里所说主要针对标准C语言。(感谢whyglinux兄指正)


首先说明一下何谓"l-value”"r-value“
"l-value”
的定义:

引用:《TCPLA.5
An object is a named region of storage, an l-value is an expression referring to an object. 

引用:《ISO/IEC9899 WG14/N1124P58
An l-value is an expression with an object type or an incomplete type other than void.

"r-value"
的定义:

引用:《C: A reference manual
(Fifth Edition) P203: An expression that is not an l-value is sometimes called an r-value because it can be only on the right-hand side of an assignment.

可以看出《TCPL》和《C99 WG14》(准标准文档)对"lvalue"的定义有一定的变化,后者更宽泛一些。

"l-value"
的称谓来自于赋值运算,

引用:《ISO/IEC9899 WG14/N1124P58
The name "l-value" comes originally from the assignment E1 = E2, in which the left operand E1 is required to be a (modifiable) l-value.

但是其本身表示的含义不局限于此。"lvalue"还可以分为一般的"lvalue""modifiable lvalue"

引用:注:

这里为什么不说:"lvalue"还可以分为"unmodifiable lvalue"
"modifiable lvalue" 
是因为在标准中只出现了"modifiable lvalue" 而没有出现"unmodifiable lvalue"的字眼,可以这样理解,但我个人不主张引入非标准中出现的专用名词,以免造成误解。

感谢whyglinux兄指出。


引用:《ISO/IEC9899 WG14/N1124P58 A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions)wit a const-qualified type.


"lvalue"
的概念可以用来对operators进行分析,就是看哪些operators可以产生"lvalue",哪些operators的左操作数要求是"lvalue"。例如:

引用:对于地址
 
                     (void *)0x800000FF 
*0x800000FF
就不是"lvalue",因为*0x800000FFvoid类型,但是cast operator可以产生
"lvalue":
             *(char  *)0x800000FF  
"lvalue",并且是"modifiable lvalue"

         *(char  *)0x800000FF = ‘a';
引用:注:非常对不起,这个例子是不正确的。whyglinux兄指出:

[color=Blue]0x800000FF
是整型,不是指针类型,因此 *0x800000FF 是非法的,还谈不上是左值还是右值的问题。

你可能是想说明 *(void*)0x800000FF,不过显然也是非法的。

另外,你说的“cast operator可以产生"lvalue"”是错误的。恰恰相反,在标准 C  C++ 中,cast operator 操作结果产生的是右值。
[/color]
其实cast operator产生左值只是编译器(如GCC)的extentions,标准C中在脚注中明确指出:

"A cast does not yield an lvalue."
(谢谢whyglinux兄斧正)


对于赋值运算,要求其左操作数必须为"lvalue"。(这个就不用多说了)


我觉得简单一点的话,"lvalue""rvalue"可以这样理解:
"lvalue"
必须对应于一块确定的内存空间,并且在编译时已经确定了;

引用:注:

这里理解有误,这句话对于《TCPL》中"lvalue"的定义可能有效,但是对于更宽泛的定义有问题。根据《ISO/IEC9899 WG14/N1124》的定义,对于非 void 的非完整类型(incomplete type)的引用是左值,因此,它与此存储空间编译时是否确定、是否真实存在以及是否能够实际访问无关。那么是否可以这样理解:

"lvalue"
必须对应于一块存储空间,并且是对非void 类型的objectreference

(感谢whyglinux兄斧正)


"rvalue"
可以理解为一段存储空间表示的值或一个表达式的值,它强调的是value的本意。
标准中建议这样理解:

引用:《ISO/IEC9899 WG14/N1124P58 footnote 53)
It (lvalue) is perhaps better considered as representing an object "locator value". What is sometimes called "rvalue" is in this International Standard described as the "value of an expression".


所以,我认为按照"rvalue"的定义,任何有确定值的量都可以作为"rvalue",只是按照C语言的标准,有些量作为"rvalue"将引发"undefined Behavior"或其他意想不到的问题。

"lvalue"可以作为"rvalue",但是"rvalue"不一定可以作为"lvalue"来使用。

引用:注:

这里的说法需要进一步说明。C标准中对"lvalue"进行了原始的定义,而"rvalue"是在"lvalue"的基础上定义的,就相当于的基础上定义为不对whyglinux兄指出的:左值和右值的概念是对立的,左值可以作为右值是因为 C  C++ 标准中规定的 lvalue-to-rvalue 转换所致。虽然这个我没有找到原文(有哪位知道请不吝告知),但我觉得是有道理的。所以说"lvalue"可以作为"rvalue"(但不是所有的适合,因为对"lvalue"求值可能得到垃圾数据或造成非法操作)。另一方面,一个表达式是"lvalue"可以说是天生的,但是一个表达式是"rvalue"却存在两种情况:一类是天生的(比如整型常量100);而还有一类是"lvalue"转换来的,这一类的"rvalue"其实既可以称为"rvalue"(当然要在具体上下文环境中,因为"rvalue"的定义就是和合法的赋值运算紧密联系在一起的)又可以称为"lvalue",所以这一类的"rvalue"是可以作为"lvalue"来使用的。因此这里说“"rvalue"不一定可以作为"lvalue"来使用主要是考虑这种情况。

例如:

int x;


int y = 10;


x = y;           /*
这里变量y按定义是"lvalue",但是转化为"rvalue"来使用,针对赋值运算来说也可以称为


              "rvalue"*/


y = 100;       /*
这里y按其本来的类型作为"lvalue"来使用*/


(感谢whyglinux兄指出)


举几个例子:
1
"hello world"string literal(如帖中whyglinux兄所说),并且是数组类型,C标准说string literal 将存储在静态存储区,并且其元素具有char类型(注意:标准《ISO/IEC9899 WG14/N1124》并没有说其元素具有const char类型,其实在老版本的C中没有const关键字,它是从C++中借鉴过来的)。但是"hello world"确实是表示了同const char *相同的意思。所以可以这么说吧。有点扯远了,那么"hello world"是不是"lvalue"?答案是:它是不可修改的"lvalue"。可以这样来分析:首先"hello world"具有确定的数据类型,然后它所在的内存地址在编译的时候已经确定了(在静态存储区的某段连续的空间)。

2
、看以下一段代码:


char *p;


*p = 'a';


printf("%c\n", *p);


大家都能看出这是一段错误的代码,其原因可以归结为"incorrect use of assignment",因为*p不是
"lvalue"
,因为p指向了一个在编译期间无法确定的object,因而*p所对应的内存空间是不确定的。

引用:注:

这里解释完全错误。根据《ISO/IEC9899 WG14/N1124》对"lvalue"的定义,*p应该是"lvalue"

(感谢wolf0403兄斧正)


那么*p"rvalue"吗?答案是:它按定义来说是"rvalue"。因为*p不管怎样在每次运行的时候都会有值存在(只不过是垃圾数据或会导致非法访问)。所以应该说:*p"rvalue"但是不能在实际中当"rvalue"来用。

那么p是不是"lvalue"呢?这是当然了,它是"lvalue"而且还是"modifiable lvalue",要不然如何初始化呢?

引用:注:要不然如何初始化呢一句中的初始化应该是赋值,不是笔误,是针对下一句代码的,是思路错误。呵呵。

(感谢whyglinux兄斧正)


原因也可以像以上所示方法分析。

p = malloc(BUFSIZ * sizeof (*p));


3
"lvalue"可以作为"rvalue",但不是所有的都可以。以下代码:
引用:注:这里有错误。任何函数调用不是"lvalue",还有标准也明确给出函数名(function designator)不是左值,因为function designator function type而不是object type,所以它也不是"lvalue"

例子应该是:不能作为"rvalue"的例子。

(感谢whyglinux兄斧正)


void foo(void)


{


   /*do something...*/


}


int i;


i = foo();          /*function call
是表达式,foo()"lvalue"但在这里不能作为"rvalue"*/


这里的foo()函数调用不是"lvalue"

 


4
、两个天生就是"rvalue"的例子

#define CONSTANT 10


int num;




num = 3;                /*
整数、浮点数、字符(不是字符串)常量天生"rvalue"*/


num = CONSTANT; /*
宏定义的常量也是"rvalue"*/



其实我觉得深入了解C标准中的一些原始定义,不仅可以更深入地了解C语言本身,而且在查找问题的时候可以直接深入其根源,这样就能更容易地找到治本的良方了。这方面我觉得国内的朋友相比国外的同行来说应该加油了,一个很深刻的例子是:有一次,我在comp.lang.c上看到一位网友发帖问问题的时候对一段代码给出了这么一句话:


int result;


int x = 3;


int y = 4;


result = max(x, y);



The parameters of the function max in the function call statement...
马上就有人指出这里的"parameters"用错了,应该是"arguments",因为"parameters"是用来描述形参,而"arguments"是用来描述实参的。这可能有语言上的差异,但有一点是相通的,那就是"argument""parameter"C标准中具有不同的意思。

(第一次更正)特别感谢楼下的wolf0403whyglinux兄的帮助,希望有更多的朋友给出见解或直接指正,我将积极以这种形式(保留错误以供后车之鉴)修改原文。

(第二次更正)非常感谢whyglinux兄的大力支持和不遗余力的指导。继续修改了一些明显的错误。欢迎更多的

朋友加入完善。

以上仅为个人理解,欢迎各位拍砖。

Any comments welcome!

本帖最后由 kernelxu  2006-5-5 16:13 编辑
 ]

 wolf0403 回复于:2006-05-03 19:29:14

引用:2、看以下一段代码:

char *p;


*p = 'a';


printf("%c\n", *p);


大家都能看出这是一段错误的代码,其原因可以归结为"incorrect use of assignment",因为*p不是
"lvalue"
,因为p指向了一个在编译期间无法确定的object,因而*p所对应的内存空间是不确定的。



我认为这一段说法不确切。 char *p 声明后,*p 不论何时,都是一个 lvalue。否则,*p = 'a'; 将引发的是一个编译错误,而不是运行错误。
语言标准规定的范畴在于源码层次而不在二进制层次。虽然 *p = 'a' 这样的代码在通常环境中可能会运行出错,但是 C 语言并没有规定这样是不允许的。在一个无虚拟内存的环境中,这个指令也许可以将 p 所指向的位置(如 p 在栈上声明,则应为随机值)赋值为 'a' 


本帖最后由 wolf0403  2006-5-3 19:32 编辑 ]

 mik 回复于:2006-05-03 20:07:27

说这么多干嘛


简单问题复杂化,没必要

 whyglinux 回复于:2006-05-04 00:28:32

要准确理解左值和右值的概念,首先需要明确我们指的是 C 还是 C++ 中的左值或右值。这是因为 C  C++ 对于左值及右值的定义是有区别的。另外,左值和右值的概念人为规定的成份很大,往往给出的定义不能囊括所有情况。


>> 
TCPLA.5
An object is a named region of storage, an l-value is an expression referring to an object.

对于 C 语言来说,这个定义就不是太准确,特别在 C99 标准出台之后更是如此。这个定义中规定了对象(object有名存储区(a named region of storage。且不说动态分配的内存(无名存储区)能否作为对象,只就 string literal 以及在 C99 中新增加的 Compound litertal 而言(它们都是对象,并且都是左值,但是又都是没有名字表示的),就不在上述定义的界定范围之内。


对于 C++ 来说,这个定义就更不适用了。因为 C++ 中对象也可能是一个非左值,即右值。同理,ISO/IEC9899 中为 C 语言提供的左值的定义也不适用于 C++


>> "lvalue"
还可以分为一般的"lvalue""modifiable lvalue"


这句话说得比较别扭。是否可改为 "lvalue" 还可以分为"modifiable lvalue"  "unmodifiable lvalue"


>> "lvalue"
必须对应于一块确定的内存空间,并且在编译时已经确定了


ISO/IEC9899 WG14/N1124P58
An l-value is an expression with an object type or an incomplete type other than void.

根据上述定义,对于非 void 的非完整类型(incomplete type)的引用也是左值;而我们知道在程序中对于非完整类型是不能进行引用的,否则会在编译时产生错误。


因此,左值只是对应着某一存储空间,而与此存储空间是否真实存在、是否能够实际访问无关,更谈不上是在编译时确定的了——这显然否定了动态分配的对象也可以是左值。


>> "lvalue"
可以作为"rvalue",但是"rvalue"不一定可以作为"lvalue"来使用。


左值和右值的概念是对立的,即非左即右(根据C++标准对左值的定义,C标准没有明确这么说)。左值可以作为右值是因为 C  C++ 标准中规定的 lvalue-to-rvalue 转换所致,但是右值不是不一定、是一定不能作为左值来使用。


>> 
它是"lvalue"而且还是"modifiable lvalue",要不然如何初始化呢?


左值对象都可以被初始化,即使是对于不能改变的左值也是如此。否则,如果不能初始化你又如何使用它呢?因为未初始化就使用是非法的。显然上面一句的初始化应该是赋值的笔误。



了解了左值和右值的概念,说明对于语言又有了更深的理解。其好处就是:对于在编程中遇到的一些问题,原来可能只知其然,现在可以做到知其所以然

 kernelxu 回复于:2006-05-04 15:42:26

引用:原帖由 wolf0403  2006-5-3 19:29 发表



我认为这一段说法不确切。 char *p 声明后,*p 不论何时,都是一个 lvalue。否则,*p = 'a'; 将引发的是一个编译错误,而不是运行错误。

语言标准规定的范畴在于源码层次而不在二进制层次。虽然
 *p = 'a'  ... 


你是对的,谢谢指正。我已将原文修改。

 kernelxu 回复于:2006-05-04 15:52:02

引用:要准确理解左值和右值的概念,首先需要明确我们指的是 C 还是 C++ 中的左值或右值。这是因为 C  C++ 对于左值及右值的定义是有区别的。另外,左值和右值的概念人为规定的成份很大,往往给出的定义不能囊括所有情况。


谢谢指出,目前对C++的标准还不甚了解,我的本意都是针对标准C的。

引用:>> "lvalue"还可以分为一般的"lvalue""modifiable lvalue"


这句话说得比较别扭。是否可改为 "lvalue" 还可以分为"modifiable lvalue"  "unmodifiable lvalue"


这里主要是考虑尽量不误导大家,因为标准C中明确出现了"modifiable lvalue",但我自己在标准中还没找到"unmodifiable lvalue",不知是否是自己遗漏。

引用:>> "lvalue"可以作为"rvalue",但是"rvalue"不一定可以作为"lvalue"来使用。


左值和右值的概念是对立的,即非左即右(根据C++标准对左值的定义,C标准没有明确这么说)。左值可以作为右值是因为 C  C++ 标准中规定的 lvalue-to-rvalue 转换所致,但是右值不是不一定、是一定不能作为左值来使用。


可能是表述不清吧,具体解释请参看原文修改的部分。若有不同意见请不吝赐教。

引用:>> 它是"lvalue"而且还是"modifiable lvalue",要不然如何初始化呢?


左值对象都可以被初始化,即使是对于不能改变的左值也是如此。否则,如果不能初始化你又如何使用它呢?因为未初始化就使用是非法的。显然上面一句的初始化应该是赋值的笔误。


呵呵,这都看出来了,想必whyglinux兄确是一位一丝不苟之人啊,敬佩!
这里不是笔误,而是思路歪了呵呵,谢谢指正。


引用:了解了左值和右值的概念,说明对于语言又有了更深的理解。其好处就是:对于在编程中遇到的一些问题,原来可能只知其然,现在可以做到知其所以然
 

严重同意,知音啊。呵呵


本帖最后由 kernelxu  2006-5-4 16:10 编辑 ]

 小猴儿 回复于:2006-05-05 04:19:08

不错, 收藏了~~

 whyglinux 回复于:2006-05-05 13:14:15

>> "lvalue"
还可以分为一般的"lvalue""modifiable lvalue"

>> 
这里为什么不说:"lvalue"还可以分为"unmodifiable lvalue"
"modifiable lvalue"
>> 
是因为在标准中只出现了"modifiable lvalue" 而没有出现"unmodifiable lvalue"的字眼,可以这样理解,但我个人不主张引入非标准中出现的专用名词,以免造成误解。


在标准中也没有一般的lvalue”一说。不理解你说的一般的lvalue”指的是什么:是"modifiable lvalue"还是非"modifiable lvalue"?抑或是同时指两者?


>> *0x800000FF
就不是"lvalue",因为*0x800000FFvoid类型,但是cast operator可以产生
"lvalue"

0x800000FF
是整型,不是指针类型,因此 *0x800000FF 是非法的,还谈不上是左值还是右值的问题。


你可能是想说明 *(void*)0x800000FF,不过显然也是非法的。


另外,你说的“cast operator可以产生"lvalue"”是错误的。恰恰相反,在标准 C  C++ 中,cast operator 操作结果产生的是右值。


>> 
那么是否可以这样理解:

>> "lvalue"
必须对应于一块存储空间,并且是对非void 类型的objectreference


注意标准中说的是“An l-value is an expression with an object type or an incomplete type other than void”。因此,强调的是非 void  [color=red]object type[/color]  [color=red]incomplete type[/color],而不是实际存在的 [color=red]object[/color]。我在前面已经说过,左值概念与对象(存储空间)的实际存在与否无关。当然了,if an lvalue does not designate an object when it is evaluated, the behavior is undefined


>> "rvalue"
可以理解为一段存储空间表示的值或一个表达式的值,它强调的是value的本意。


一段存储空间表示的值其实就是对象的值。获得对象的值的过程我们通常称之为求值。对对象求值是一个由左值转化为右值的过程(lvalue-to-rvalue),所以对象的值是右值,但不能说对象是右值。关于 lvalue-to-rvalue 转化在 C99 标准的 6.3.2.1-2  -3 一节中叙述。


一个单独的对象也可以作为表达式使用,所以上述这句话可简写为“"rvalue"可以理解为表达式的值。这也是标准中出现的描述。


>> "lvalue"
可以作为"rvalue",但不是所有的都可以。


lvalue-to-rvalue 
转化是标准规定的。lvalue 转换为 rvalue 实际上就是对左值求值的过程。存在不能对其求值的左值吗?


你在上面举的例子也说明不了你这句话的正确性。因为在 C 中,所有的函数调用产生的都是右值,而不是你说的左值(在 C++ 中,除了返回引用的函数调用是左值外,其它的函数调用结果也都是右值)。

 kernelxu 回复于:2006-05-05 15:56:57

首先非常感谢whyglinux兄的大力支持和不遗余力的指导。


引用:在标准中也没有一般的lvalue”一说。不理解你说的一般的lvalue”指的是什么:是"modifiable lvalue"还是非"modifiable lvalue"?抑或是同时指两者?


我查找到的原文这样说的:
引用:
regular "lvalue" and "modifiable lvalue"
(当然这里不是说"regular lvalue""modifiable lvalue",因为没有"regular lvalue"这一术语。是在下直译过来的)


可能这样说比较好一点吧:"lvalue"中包含一类特殊的"lvalue"——"modifiable lvalue"。我觉得理解起来也可以像您那样说:
引用:"modifiable lvalue"和非
"modifiable lvalue"

引用:0x800000FF是整型,不是指针类型,因此 *0x800000FF 是非法的,还谈不上是左值还是右值的问题。

你可能是想说明 *(void*)0x800000FF,不过显然也是非法的。

另外,你说的“cast operator可以产生"lvalue"”是错误的。恰恰相反,在标准 C  C++ 中,cast operator 操作结果产生的是右值。


谢谢斧正,这个例子用来说明some operators can yield lvalue是错误的,这是其一;另一方面,说cast operator 产生"lvalue"是错误的,它产生左值是编译器(如GCC)的extentions,标准C中在脚注中明确指出:
引用:
 "A cast does not yield an lvalue."  
(因为标准中的脚注并不是标准的正式文本,才导致有的编译器另行对待吧)


呵呵还是想举个例子,比如说indirection operator产生"lvalue"(这里的产生并非指从"lvalue""lvalue"),这个例子不知道对不对,请不吝赐教:

#include 


#include 




char *mystrcp(char *dest, const char *src)


{


        char *temp;


        temp = dest;


        while ((*dest++ = *src++) != '\0')


            ;


       


        return dest;


}




int main(int argc, char *argv[])


{


    char *dest = malloc(100 * sizeof (*dest));


    char *src  = "Hello World";


        


    *mystrcp(dest, src) = 'I';  /********************/


    printf("dest: %s\n", dest);


        


    system("pause");


    return 0;


}


这段代码在DEV-CPP4.9.9.2VC++6.0下试验未出现任何问题。首先函数调用肯定不是"lvalue",这里的*operator将产生一个"lvalue"
引用:>> 那么是否可以这样理解:

>> "lvalue"
必须对应于一块存储空间,并且是对非void 类型的objectreference


注意标准中说的是“An l-value is an expression with an object type or an incomplete type other than void”。因此,强调的是非 void  object type  incomplete type,而不是实际存在的 object。我在前面已经说过,左值概念与对象(存储空间)的实际存在与否无关。当然了,if an lvalue does not designate an object when it is evaluated, the behavior is undefined


您说得很对,但这里我并未说object必须是实际存在的object啊。不过你这一段,更能加深大家的理解。还是很感谢。
引用:>> "rvalue"可以理解为一段存储空间表示的值或一个表达式的值,它强调的是value的本意。


一段存储空间表示的值其实就是对象的值。获得对象的值的过程我们通常称之为求值。对对象求值是一个由左值转化为右值的过程(lvalue-to-rvalue),所以对象的值是右值,但不能说对象是右值。关于 lvalue-to-rvalue 转化在 C99 标准的 6.3.2.1-2  -3 一节中叙述。


嗯,这里我觉得可能啰嗦了一点但是并没有歧义,一段内存空间表示的值最终还是的意思。好谢谢指明转化的位置。

引用:一个单独的对象也可以作为表达式使用,所以上述这句话可简写为“"rvalue"可以理解为表达式的值。这也是标准中出现的描述。


对,标准在脚注中说的原文是:
引用:
What is sometimes called "rvalue" is in this International Standard described as the "value of an expression".

以上那样说只是我自己的理解:一段存储空间表示的值是我想强调一类由"lvalue" 转化来的"rvalue"吧。

引用:>> "lvalue"可以作为"rvalue",但不是所有的都可以。


lvalue-to-rvalue 
转化是标准规定的。lvalue 转换为 rvalue 实际上就是对左值求值的过程。存在不能对其求值的左值吗?

你在上面举的例子也说明不了你这句话的正确性。因为在 C 中,所有的函数调用产生的都是右值,而不是你说的左值(在 C++ 中,除了返回引用的函数调用是左值外,其它的函数调用结果也都是右值)。
 

嗯,首先承认错误,任何函数调用不是"lvalue",还有标准也明确给出函数名(function designator)不是左值,因为function designator function type而不是object type,所以它也不是"lvalue"。但有一点是:不是"lvalue"就是"rvalue"吗?

那么这个例子中:

void foo(void)


{


   /*do something...*/


}


int i;


i = foo();          /*function call
是表达式,函数调用foo()不是"lvalue"但在这里也不能作为"rvalue"*/

 

 whyglinux 回复于:2006-05-05 16:27:50

>> 
比如说indirection operator产生"lvalue"(这里的产生并非指从"lvalue""lvalue"

>> *mystrcp(dest, src)

解引用的 * 运算符要求其操作数仅是一个右值,因此 *pp是左值对象,可转化为右值)以及 *f()f()是右值)的结果都是左值。


>> i = foo();          /*function call
是表达式,函数调用foo()不是"lvalue"但在这里也不能作为
"rvalue"*/

之所以不能这样赋值是因为类型的问题:函数 foo() 的返回值为 void,而 i 的类型为 int。如果单独调用函数的话 foo();,那函数返回的就是一个右值。

 kernelxu 回复于:2006-05-05 19:15:44

非常高兴和您继续探讨!


引用:>> i = foo();          /*function call是表达式,函数调用foo()不是"lvalue"但在这里也不能作为
"rvalue"*/

之所以不能这样赋值是因为类型的问题:函数 foo() 的返回值为 void,而 i 的类型为 int。如果单独调用函数的话 foo();,那函数返回的就是一个右值。
 


这里我觉得其实质原因不是类型的问题,而是本身void类型的值不能作为"rvalue"来使用。


标准中没有提到可以用void来定义变量,所以

void num;


这样的定义是非法的。


the void type
的用法主要有:
引用:《CA Reference Manual 
fifth edition, P168 5.9 The void Type:
(1) as the return type of a function, signifying that the function returns no value;
(2) in a cast expression when it is desired to explicitly discard a value;
(3) to form the type void *, a "universal" data pointer; and
(4) in place of the parameter list in afunction declarator to indicate that the function takes no arguments. 

如果仅仅是类型的问题话,那么我引入cast operator是否就应改产生"rvalue"呢?但是

引用:
void foo(void)
{
   /*do something...*/
}

int i;
[color=Red]i = (int)foo();[/color]

这样也是不行的,而有另外一个例子:

int num;


num = (void)100;


(void)100
是合法的,但是这样将他作为"rvalue"就是非法的。

然后,"rvalue"的定义为"the value of an expression",但是

引用:
void as the return type of a function, signifying that the function returns [color=Red]no value;[/color]

所以我觉得单独的返回值为void的函数调用也不是"rvalue",而是"not a "lvalue" "


本帖最后由 kernelxu  2006-5-5 19:17 编辑 ]

 mik 回复于:2006-05-05 19:30:54

从编译器的角度理解往往事倍功倍,比纸上说什么都强


void n ;     
为什么会出错?


抛弃什么左值,右值的概念,从编译器角度看,void 是什辅助性的修饰,那么编译器是无法为void型的变量为配空间的。


当然,编译器也无法为
 
    int i = (int)foo();    
获得数据,应此,编译器肯定无法工作。



(void)100; 
那么编译器如何处理呢? 编译器将抛弃100这个值。这是可以理解的。



*mystrcp() = 'c';  
那么编译器将参引 *里的值;这是一个地址值,往地址里放东东,是可以理解的。


反过来看:


&i = 1;      &i 
的值是数值型的东东,当然编译器无法处理了。

 whyglinux 回复于:2006-05-05 20:01:24

>> 
我觉得单独的返回值为void的函数调用也不是"rvalue",而是"not a "lvalue" "


根据左值的定义,我们已经知道是不存在 void 类型的左值的。再看看标准对 void 类型的说明:


The void type comprises an empty set of values; it is an incomplete type that cannot be
completed. (C99 6.2.5-19)

这是说类型为 void 的对象实际上是不存在的。尽管 void 表示 no value (或者是 empty),这一状态也可以看作是一种特殊的 value


而且你在前面也说过:An expression that is not an l-value is sometimes called an r-value。一个表达式的结果要么是左值,要么是右值,二者必居其一(也就是我前面说过的非左即右,这在 C++ 标准中是有明确规定的:Every expression is either an lvalue or an rvalue)。而结果类型为 void 的表达式不可能是左值(如你上面所说的"not an "lvalue" "),所以必然是右值。

 kernelxu 回复于:2006-05-05 21:02:54

讨论得很有意思,其实有时候争论往往会以聚焦到一个概念而告终,我们争论的最后一个焦点就是:

一个表达式究竟是非左值即右值,还是还存在另一种非左值也非右值的值呢

我觉得到这个层面如何归类应该视上下文环境来看。


引用:

根据左值的定义,我们已经知道是不存在 void 类型的左值的。再看看标准对 void 类型的说明:


The void type comprises an empty set of values; it is an incomplete type that cannot be
completed. (C99 6.2.5-19)

同意。

引用:这是说类型为 void 的对象实际上是不存在的。尽管 void 表示 no value (或者是 empty),这一状态也可以看作是一种特殊的 value


这样理解也无妨。
引用:

而且你在前面也说过:An expression that is not an l-value is sometimes called an r-value


这是《CA Reference Manual fifth edition的解释,而且这里有sometimes
引用:一个表达式的结果要么是左值,要么是右值,二者必居其一(也就是我前面说过的非左即右,这在 C++ 标准中是有明确规定的:Every expression is either an lvalue or an rvalue)。而结果类型为 void 的表达式不可能是左值(如你上面所说的"not an "lvalue" "),所以必然是右值。

 
可能C++是如此,但是在C标准中对有些表达式都只是说
引用:
"is not a lvalue"

而没有直接说

引用:
"is a rvalue"

例如:

引用:《ISO/IEC9899 WG14/N1124
6.3.2.1 
...an lvalue that does not have array type is converted to the value stored in the designated object [color=Red](and is no longer an lvalue).[/color]
...an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and[color=Red] is not an lvalue.[/color]


而且,C标准没有明确定义"rvalue"(只在脚注中提到)

引用:《ISO/IEC9899 WG14/N1124
6.3.2.1 
What is [color=Red]sometimes called ‘‘rvalue’’ [/color]is in this International Standard described as the ‘‘value of an expression’’.

"rvalue"
只是帮助更好地理解"lvalue"而出现,所以标准中不是"lvalue"都以"no lvalue"来表述。

所以我相信这个not a lvalue不仅包括"rvalue"还包括"no value and no rvalue"的一类。


其实,到这里只是个人理解不一样而已,因为标准也像其他很多东西一样并没有精确定义。

很高兴和你探讨。:)

 kernelxu 回复于:2006-05-05 21:14:49

引用:原帖由 mik  2006-5-5 19:30 发表

从编译器的角度理解往往事倍功倍,比纸上说什么都强


void n ;     
为什么会出错?


抛弃什么左值,右值的概念,从编译器角度看,void 是什辅助性的修饰,那么编译器是无法为void型的变量为配空间的。


当然
 ... 



呵呵,其实我觉得编译器只是标准的实现而已,一个自称和标准conformable的编译器应该遵循标准明确定义的rules,而将其他的功能作为extensions


引用:从编译器的角度理解往往事倍功倍,比纸上说什么都强


编写编译器的人需要熟读和理解标准的每一句话,编译器有成千上万,而一种语言的标准只有一种,谁是根源呢?
引用:void n ;     为什么会出错?


抛弃什么左值,右值的概念,从编译器角度看,void 是什辅助性的修饰,那么编译器是无法为void型的变量为配空间的。


当然,编译器也无法为
 
    int i = (int)foo();    
获得数据,应此,编译器肯定无法工作。



(void)100; 
那么编译器如何处理呢? 编译器将抛弃100这个值。这是可以理解的。


实际上是标准命令编译器这么干的。

引用:*mystrcp() = 'c';  那么编译器将参引 *里的值;这是一个地址值,往地址里放东东,是可以理解的。


反过来看:


&i = 1;      &i 
的值是数值型的东东,当然编译器无法处理了。
 

能不能这样用是由assignment operator的属性决定的,赋值运算要求其left operandmodifiable lvalue

right operand 
rvalue

因为

引用:
*mystrcp() = 'c';
*mystrcp() 
lvalue;且'c'
rvalue
int i;
&i = 1; 
&i is not a modifiable lvalue.


本帖最后由 kernelxu  2006-5-5 21:25 编辑 ]

 mik 回复于:2006-05-05 21:26:28

我是从编译器角度理解标准 :)

 kernelxu 回复于:2006-05-05 22:57:30

讨论得很有意思,其实有时候争论往往会以聚焦到一个概念而告终,我们争论的最后一个焦点就是:

一个表达式究竟是非左值即右值,还是还存在另一种非左值也非右值的值呢

我觉得到这个层面如何归类应该视上下文环境来看。


引用:

根据左值的定义,我们已经知道是不存在 void 类型的左值的。再看看标准对 void 类型的说明:


The void type comprises an empty set of values; it is an incomplete type that cannot be
completed. (C99 6.2.5-19)

同意。

引用:这是说类型为 void 的对象实际上是不存在的。尽管 void 表示 no value (或者是 empty),这一状态也可以看作是一种特殊的 value


这样理解也无妨。
引用:

而且你在前面也说过:An expression that is not an l-value is sometimes called an r-value


这是《CA Reference Manual fifth edition的解释,而且这里有sometimes
引用:一个表达式的结果要么是左值,要么是右值,二者必居其一(也就是我前面说过的非左即右,这在 C++ 标准中是有明确规定的:Every expression is either an lvalue or an rvalue)。而结果类型为 void 的表达式不可能是左值(如你上面所说的"not an "lvalue" "),所以必然是右值。

 
可能C++是如此,但是在C标准中对有些表达式都只是说
引用:
"is not a lvalue"

而没有直接说

引用:
"is a rvalue"

例如:

引用:《ISO/IEC9899 WG14/N1124
6.3.2.1 
...an lvalue that does not have array type is converted to the value stored in the designated object [color=Red](and is no longer an lvalue).[/color]
...an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and[color=Red] is not an lvalue.[/color]


而且,C标准没有明确定义"rvalue"(只在脚注中提到)

引用:《ISO/IEC9899 WG14/N1124
6.3.2.1 
What is [color=Red]sometimes called ‘‘rvalue’’ [/color]is in this International Standard described as the ‘‘value of an expression’’.

"rvalue"
只是帮助更好地理解"lvalue"而出现,所以标准中不是"lvalue"都以"no lvalue"来表述。

所以我相信这个not a lvalue不仅包括"rvalue"还包括"no value and no rvalue"的一类。


其实,到这里只是个人理解不一样而已,因为标准也像其他很多东西一样并没有精确定义。

很高兴和你探讨。:)

 whyglinux 回复于:2006-05-05 23:42:16

>> C
标准没有明确定义"rvalue"(只在脚注中提到)

>> 
因为标准也像其他很多东西一样并没有精确定义。


确实是这样的,特别是对于 C 语言来说更是如此。


正因为 "rvalue" 不是在 C 标准正文中规定的正式术语,所以在标准中出现的时候也尽量避免使用这个术语,而是一般说成“not an lvalue” 或者其它否定形式。


可能对此有不同的看法,不过我认为可以把 “not an lvalue” 等价于 rvalue。在 [url=http://publib.boulder.ibm.com/infocenter/pseries/v5r3/topic/com.ibm.xlcpp8a.doc/language/ref/lvalue.htm]IBM 提供的资料上看到这么一句话:Rvalues always have complete types or the void type. 支持我的上述看法。不管 C 标准是怎么对待 rvalue 的,实际中它已经变成了一个正式用语。


>> 
能不能这样用是由assignment operator的属性决定的,赋值运算要求其left operandmodifiable lvalueright operand rvalue


还要再加上一个条件,即左右操作数的类型是否一致,或者右操作数的类型是否可以默认转换为左操作数的类型。


>> &i = 1;
>> &i is not a modifiable lvalue.

更严密的说法是:&i is not an lvalue。或者直接说 &i is an rvalue

 kernelxu 回复于:2006-05-06 12:56:27

引用:原帖由 whyglinux  2006-5-5 23:42 发表

>> C
标准没有明确定义"rvalue"(只在脚注中提到)

>> 
因为标准也像其他很多东西一样并没有精确定义。


确实是这样的,特别是对于 C 语言来说更是如此。


正因为 "rvalue" 
 ... 

引用:>> C标准没有明确定义"rvalue"(只在脚注中提到)

>> 
因为标准也像其他很多东西一样并没有精确定义。


确实是这样的,特别是对于 C 语言来说更是如此。


正因为 "rvalue" 不是在 C 标准正文中规定的正式术语,所以在标准中出现的时候也尽量避免使用这个术语,而是一般说成“not an lvalue” 或者其它否定形式。


可能对此有不同的看法,不过我认为可以把 “not an lvalue” 等价于 rvalue。在 IBM 提供的资料上看到这么一句话:Rvalues always have complete types or the void type. 支持我的上述看法。不管 C 标准是怎么对待 rvalue 的,实际中它已经变成了一个正式用语。



嗯,其实有这么多不同的理解就是由于标准没有定义清楚的缘故。只要基本意思一样也就无妨了。而且有的两种理解的同时存在呢,呵呵。刚刚从whyglinux兄所给的IBM链接找到这么一句话(不过是针对C++的),还是觉得理解第一吧,呵呵。
链接是[url=http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.xlcpp8a.doc/language/ref/lvalue.htm][color=Red]IBM AIX Compiler Information[/color](感谢whyglinux提供这么好的资源链接)

 
引用:
On the other hand, in C++, a function call that returns a reference is an lvalue. Otherwise, a function call is an rvalue expression. [color=Red]In C++, every expression produces an lvalue, an rvalue, or no value.[/color]



引用:>> 能不能这样用是由assignment operator的属性决定的,赋值运算要求其left operandmodifiable lvalueright operand rvalue


还要再加上一个条件,即左右操作数的类型是否一致,或者右操作数的类型是否可以默认转换为左操作数的类型。



嗯,谢谢指出。
引用:
>> &i = 1;
>> &i is not a modifiable lvalue.

更严密的说法是:&i is not an lvalue。或者直接说 &i is an rvalue


好,接受。谁让我在 &i = 1; 前明确加了一个 int i; 呢。呵呵。

阅读(1461) | 评论(0) | 转发(0) |
0

上一篇:2011-04-01

下一篇:C语言中的lvalue, rvalue

给主人留下些什么吧!~~