http://guliqun1983.blog.163.com/blog/static/501116852011730535314/
全部博文(120)
分类: C/C++
2011-04-02 10:57:41
发现很多朋友对"lvalue"和"rvalue"理解有误,我先谈谈自己对此的一些理解,并期望能够引起更多朋友的广泛讨论。也算起到抛砖引玉的作用吧。
引用:注:
这里所说主要针对标准C语言。(感谢whyglinux兄指正)
首先说明一下何谓"l-value”和"r-value“。
"l-value”的定义:
引用:《TCPL》A.5:An object is a named region of storage, an l-value is an expression referring to an object.
引用:《ISO/IEC9899 WG14/N1124》P58: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/N1124》P58: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/N1124》P58: 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",因为*0x800000FF是void类型,但是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 类型的object的reference。
(感谢whyglinux兄斧正)
"rvalue"可以理解为一段存储空间表示的值或一个表达式的值,它强调的是value的本意。
标准中建议这样理解:
引用:《ISO/IEC9899 WG14/N1124》P58 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标准中具有不同的意思。
(第一次更正)特别感谢楼下的wolf0403和whyglinux兄的帮助,希望有更多的朋友给出见解或直接指正,我将积极以这种形式(保留错误以供后车之鉴)修改原文。
(第二次更正)非常感谢whyglinux兄的大力支持和不遗余力的指导。继续修改了一些明显的错误。欢迎更多的
朋友加入完善。
以上仅为个人理解,欢迎各位拍砖。
Any comments welcome!
[ 本帖最后由 kernelxu 于 2006-5-5 16:13 编辑 ]
回复于: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'; 将引发的是一个编译错误,而不是运行错误。
C 语言标准规定的范畴在于源码层次而不在二进制层次。虽然 *p = 'a' 这样的代码在通常环境中可能会运行出错,但是 C 语言并没有规定这样是不允许的。在一个无虚拟内存的环境中,这个指令也许可以将 p 所指向的位置(如 p 在栈上声明,则应为随机值)赋值为 'a' 。
[ 本帖最后由 wolf0403 于 2006-5-3 19:32 编辑 ]
回复于:2006-05-03 20:07:27
说这么多干嘛
简单问题复杂化,没必要
回复于:2006-05-04 00:28:32
要准确理解左值和右值的概念,首先需要明确我们指的是 C 还是 C++ 中的左值或右值。这是因为 C 和 C++ 对于左值及右值的定义是有区别的。另外,左值和右值的概念人为规定的成份很大,往往给出的定义不能囊括所有情况。
>> 《TCPL》A.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/N1124》P58: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",要不然如何初始化呢?
左值对象都可以被初始化,即使是对于不能改变的左值也是如此。否则,如果不能初始化你又如何使用它呢?因为未初始化就使用是非法的。显然上面一句的“初始化”应该是“赋值”的笔误。
了解了左值和右值的概念,说明对于语言又有了更深的理解。其好处就是:对于在编程中遇到的一些问题,原来可能“只知其然”,现在可以做到“知其所以然”。
回复于:2006-05-04 15:42:26
引用:原帖由 wolf0403 于 2006-5-3 19:29 发表
我认为这一段说法不确切。 char *p 声明后,*p 不论何时,都是一个 lvalue。否则,*p = 'a'; 将引发的是一个编译错误,而不是运行错误。
C 语言标准规定的范畴在于源码层次而不在二进制层次。虽然 *p = 'a' ...
你是对的,谢谢指正。我已将原文修改。
回复于: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
不错, 收藏了~~
回复于: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",因为*0x800000FF是void类型,但是cast operator可以产生"lvalue"
0x800000FF是整型,不是指针类型,因此 *0x800000FF 是非法的,还谈不上是左值还是右值的问题。
你可能是想说明 *(void*)0x800000FF,不过显然也是非法的。
另外,你说的“cast operator可以产生"lvalue"”是错误的。恰恰相反,在标准 C 和 C++ 中,cast operator 操作结果产生的是右值。
>> 那么是否可以这样理解:
>> "lvalue"必须对应于一块存储空间,并且是对非void 类型的object的reference。
注意标准中说的是“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++ 中,除了返回引用的函数调用是左值外,其它的函数调用结果也都是右值)。
回复于: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.2和VC++6.0下试验未出现任何问题。首先函数调用肯定不是"lvalue",这里的*operator将产生一个"lvalue"。
引用:>> 那么是否可以这样理解:
>> "lvalue"必须对应于一块存储空间,并且是对非void 类型的object的reference。
注意标准中说的是“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"*/
回复于:2006-05-05 16:27:50
>> 比如说indirection operator产生"lvalue"(这里的产生并非指从"lvalue"到"lvalue")
>> *mystrcp(dest, src)
解引用的 * 运算符要求其操作数仅是一个右值,因此 *p(p是左值对象,可转化为右值)以及 *f()(f()是右值)的结果都是左值。
>> i = foo(); /*function call是表达式,函数调用foo()不是"lvalue"但在这里也不能作为"rvalue"*/
之所以不能这样赋值是因为类型的问题:函数 foo() 的返回值为 void,而 i 的类型为 int。如果单独调用函数的话 foo();,那函数返回的就是一个右值。
回复于: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的用法主要有:
引用:《C:A 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 编辑 ]
回复于:2006-05-05 19:30:54
从编译器的角度理解往往事倍功倍,比纸上说什么都强
void n ; 为什么会出错?
抛弃什么左值,右值的概念,从编译器角度看,void 是什辅助性的修饰,那么编译器是无法为void型的变量为配空间的。
当然,编译器也无法为
int i = (int)foo(); 获得数据,应此,编译器肯定无法工作。
(void)100; 那么编译器如何处理呢? 编译器将抛弃100这个值。这是可以理解的。
*mystrcp() = 'c'; 那么编译器将参引 *里的值;这是一个地址值,往地址里放东东,是可以理解的。
反过来看:
&i = 1; &i 的值是数值型的东东,当然编译器无法处理了。
回复于: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" "),所以必然是右值。
回复于: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。
这是《C:A 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"的一类。
其实,到这里只是个人理解不一样而已,因为标准也像其他很多东西一样并没有精确定义。
很高兴和你探讨。:)
回复于: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 operand是modifiable 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 编辑 ]
回复于:2006-05-05 21:26:28
我是从编译器角度理解标准 :)
回复于: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。
这是《C:A 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"的一类。
其实,到这里只是个人理解不一样而已,因为标准也像其他很多东西一样并没有精确定义。
很高兴和你探讨。:)
回复于:2006-05-05 23:42:16
>> C标准没有明确定义"rvalue"(只在脚注中提到)
>> 因为标准也像其他很多东西一样并没有精确定义。
确实是这样的,特别是对于 C 语言来说更是如此。
正因为 "rvalue" 不是在 C 标准正文中规定的正式术语,所以在标准中出现的时候也尽量避免使用这个术语,而是一般说成“not an lvalue” 或者其它否定形式。
可能对此有不同的看法,不过我认为可以把 “not an lvalue” 等价于 rvalue。在 [url=]IBM 提供的资料上看到这么一句话:Rvalues always have complete types or the void type. 支持我的上述看法。不管 C 标准是怎么对待 rvalue 的,实际中它已经变成了一个正式用语。
>> 能不能这样用是由assignment operator的属性决定的,赋值运算要求其left operand是modifiable lvalue,right operand 是rvalue。
还要再加上一个条件,即左右操作数的类型是否一致,或者右操作数的类型是否可以默认转换为左操作数的类型。
>> &i = 1;
>> &i is not a modifiable lvalue.
更严密的说法是:&i is not an lvalue。或者直接说 &i is an rvalue。
回复于: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=][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 operand是modifiable lvalue,right operand 是rvalue。
还要再加上一个条件,即左右操作数的类型是否一致,或者右操作数的类型是否可以默认转换为左操作数的类型。
嗯,谢谢指出。
引用:>> &i = 1;
>> &i is not a modifiable lvalue.
更严密的说法是:&i is not an lvalue。或者直接说 &i is an rvalue。
好,接受。谁让我在 &i = 1; 前明确加了一个 int i; 呢。呵呵。