c语言中字符串关于左值,关于左值"lvalue"和右值"rvalue"的一点理解

发现很多朋友对"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;呢。呵呵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值