免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: mickgrady
打印 上一主题 下一主题

二维数组指针,编译器到底为我们处理了多少 [复制链接]

论坛徽章:
0
51 [报告]
发表于 2011-03-17 20:58 |只看该作者
回复 47# flw2


    你给我的感觉是有点反应过度了。我并没有任何对别人的水平产生怀疑的意思,我所说的“期望”,其实指的是:在前几个贴子中我注意到你数次提到数组到指针的转换,由此对你如何使用这个条款所产生的期望。如果我有任何言语不当的地方从而冒犯了你,我可以预先给你道个歉。

  在继续阐述之前,我想我们应当先确立一个基点:标准相当于C/C++世界中的法律,它对于C/C++的规定是最权威的,这个你应当没有异议吧?

关于什么是左值,标准以条款的形式给出了一个定义,不过你说,你要把它理解为一个“解释”,那也好,无论是定义还是解释,关键是标准对于什么是左值作出了规定。标准所作出的这个规定,相比于左值概念刚出现时作为赋值操作符的左操作数这一意义,是一个巨大的进步,它把左值的内涵从单纯的表达式扩展到对象模型,从对象而不是操作数的角度去诠释左值,其实质是,左值“指示”出一个对象(或者一个不完整的对象),这个高度上的左值,已经与能否作为左操作数无关了,已经不能从能否放在赋值操作符左边而作为判断标准了。数组名作为数组对象的指示符,完全符合标准对左值作出的规定。

另一方面,受语言规则的限制,对象可分为可修改的对象与不可修改的对象,于是,左值也分为可修改的左值与不可修改的左值。可修改的左值由于符合赋值运算符的规定,可以作为左操作数;而一个不可修改的左值,例如数组名,当然不能作为左操作数,原因并非因为数组名不是左值,而是因为其不可修改的特性,不符合赋值运算符的规定。

C++比C又更进了一步,它把左值概念扩展到了对象模型之外,当然,这超出了这里讨论的范畴。


其次,你不要把一个转换规则错当成了一个定义。为什么存在数组到指针的转换这一规则?

在C/C++的世界里,存在三大转换规则:

第一个是左值到右值的转换;
第二个是数组到指针的转换;
第三个是函数指示符到函数指针的转换;

在C中,这三大转换规则以子条款的形式存在。为什么需要这三个条款?这是表达式计算的需要,计算机只认识数值,不认识符号,一个标识符欲参与计算,

必须作出一定的转换。把一个变量名放在表达式里,取出它的值进行运算,这就是第一个转换规则:从左值到右值的转换;但,一个数组对象无法进行取值运算,

这种操作超出了C/C++所认为的合理性,必须进行另一种转换,在C/C++看来,把数组名转换成其首元素的指针是合理的,于是,数组名在表达式中的行为就象我们

现在所使用的样子了。数组到指针的转换是为这个目的而存在的,并非为了说明数组名的左值性质如何,数组名的左值性质唯有标准作出的左值规定才能指出。

第三条转换规则由于与内容无关,我就不说了。


最后,我说一句稍微夸张一点的话:作为软件开发者,要信仰C/C++;而作为C/C++开发者,要信仰标准!

论坛徽章:
0
52 [报告]
发表于 2011-03-17 21:06 |只看该作者
本帖最后由 KBTiller 于 2011-03-17 21:08 编辑
“这段话说 T t[N], t的类型大多数时候是 T*, 而且它不是左值”,
------------------------------------- ...
supermegaboy 发表于 2011-03-16 22:49


我也是这么看的
我觉得不那么难理解
光是粒子,也是波
这没有矛盾

论坛徽章:
0
53 [报告]
发表于 2011-03-17 21:21 |只看该作者
我仔细看了半天
看出我和supermegaboy网友观点上的一个差别

假如有
int i = 3  ;
i = i +1 ;
supermegaboy 会说:i是左值,但“=”右边的 i 被从左值转成了右值
而我会说: “=”左面的 i 是左值,右面的 i 是右值

是这样吗?supermegaboy网友

论坛徽章:
0
54 [报告]
发表于 2011-03-18 09:01 |只看该作者
我也是这么看的
我觉得不那么难理解
光是粒子,也是波
这没有矛盾
KBTiller 发表于 2011-03-17 21:06



    你这个比喻不太恰当,波粒二象性是同时存在的,但一个标识符只能具有一个类型,从一个类型改变为另一个类型,必须进行转换。

我在很多场合都强调这是一个转换,不要把这理解为抠字眼,我的目的是希望阅读者要注意到,转换前和转换后的东西是不一样的!不要把结果与原因等同起来。

论坛徽章:
0
55 [报告]
发表于 2011-03-18 09:23 |只看该作者
我仔细看了半天
看出我和supermegaboy网友观点上的一个差别

假如有
int i = 3  ;
i = i +1 ;
super ...
KBTiller 发表于 2011-03-17 21:21



    对!

标准C的左值观念是基于对象的,它所强调的,用通俗一点的话来说,是一个左值应当指出一个“洞”、一个存储,从这个“洞”里取出东西,就是一个从左值到右值的转换过程,这是C/C++表达式运算的理论基础,没有它,表达式没有存在的意义。

但是,从左值到右值的转换有一个微妙的地方,并非任何时候都进行这个转换,是否进行转换要视目标操作的需求,有些操作符的操作数是不进行左值转换的。

对于int i;

无论把i放在赋值表达式的左边还是右边,i都是一个左值,这是由标准C的左值定义所规定的,并不以i在表达式的位置而不同。但是,当表达式计算需要一个数值时,i这个符号就需要进行转换,取出i里面的东西,这就完成了从左值到右值的转换过程。经过这个转换后,其结果就不再是一个对象,不再是一个左值了。

而对于赋值运算符,它并不要求左操作数的数值,而是需要在这里指出一个“洞”,一个存储,以放进东西(赋值),在这里,不进行左值到右值的转换。

论坛徽章:
0
56 [报告]
发表于 2011-03-18 10:10 |只看该作者
supermegaboy:

你解释的很清楚,标准说的那一部分我也看了
很佩服你的知识, 我可能是有点急了,但是也没有别的意思,就是觉得你没有正面我的问题

如果按照你的说法,K&R在这个问题上过时了,C标准参考手册是错的
因为它明确说了数组名不是左值(很确定的结论,不是解释),而你说它是
“例如数组名,当然不能作为左操作数,原因并非因为数组名不是左值,而是因为其不可修改的特性,不符合赋值运算符的规定。”
你说它是不可修改的左值(这个概念我理解,书上和标准都说了)
你说的左值到右值我也理解

我找了一个编译器tcc的代码
http://bellard.org/tcc/

它在处理int a[2][3]; *使得a-> *a; 类型从int [2][3] 变为int [3];当然了,确实还是非左值
/* indirection with full error checking and bound check */
ST_FUNC void indir(void)
{
    if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
        if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
            return;
        expect("pointer");
    }
    if ((vtop->r & VT_LVAL) && !nocode_wanted)
        gv(RC_INT);
    vtop->type = *pointed_type(&vtop->type);
    /* Arrays and functions are never lvalues */
    if (!(vtop->type.t & VT_ARRAY)
        && (vtop->type.t & VT_BTYPE) != VT_FUNC) {
        vtop->r |= lvalue_type(vtop->type.t);
        /* if bound checking, the referenced pointer must be checked */
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check)
            vtop->r |= VT_MUSTBOUND;
#endif
    }
}

它里面的int a[2][2]; *a就不是左值
它在遇到 xxx = 的时候会检查xxx是不是左值


xcm@u32:~/test$ cat array.c
#include <stdio.h>
int main()
{
        int a[2][3];
        *a = 1;
        return 0;
}
xcm@u32:~/test$ tcc array.c
array.c:5: error: lvalue expected

(gcc报的不是这样的结果)

我确实没想说你说错了,哪怕你认为*a是左值,因为你能给你的结论解释, 一个我不能找到精确定义的东西我不敢说对错,光论对错也没有意义
其实如果你解释C参考手册说“数组名不是左值“ 是错的,或者跟你(或者标准)说的左值不是一样的东西了,我就没什么好说的了,我相信标准,相信你。

论坛徽章:
0
57 [报告]
发表于 2011-03-18 10:16 |只看该作者
波粒二象性是同时存在的
supermegaboy 发表于 2011-03-18 09:01



    是这样吗?我的物理知识很有限,哪位懂得物理的网友给说说?
    我一直以为光在某些情况下表现为波,在另一些情况下表现为粒子

论坛徽章:
0
58 [报告]
发表于 2011-03-18 10:33 |只看该作者
本帖最后由 KBTiller 于 2011-03-18 10:51 编辑
……但一个标识符只能具有一个类型,从一个类型改变为另一个类型,必须进行转换。

我在很多场合都强调这是一个转换,不要把这理解为抠字眼,我的目的是希望阅读者要注意到,转换前和转换后的东西是不一样的!不要把结果与原因等同起来。supermegaboy 发表于 2011-03-18 09:01



    我们的原则分歧就在这里:“一个标识符只能具有一个类型

  我并不反对抠字眼,我也知道标准所谓“转换”的说法。但我始终没看到强调这种转换对程序员的意义。我觉得那只对编译器的作者有意义。(我有时甚至觉得应该有两个版本的标准,一本是给程序员的,另一本是给编译器作者)

   我以前也对左值这个概念很纠结,看了
Except when it is the operand of the sizeof operator or the unary & operator, or is a
  string literal used to initialize an array, 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 is ___not ____ an lvalue.

  我恍然意识到,离开具体的运算谈左值是没有意义的。而且我觉得那个所谓的转换,毫无必要(对于程序员来说)

  “不要把结果与原因等同起来”,但是对于程序员来说,只有“结果”这个层面的含义

论坛徽章:
0
59 [报告]
发表于 2011-03-18 23:04 |只看该作者
supermegaboy:

你解释的很清楚,标准说的那一部分我也看了
很佩服你的知识, 我可能是有点急了,但是也 ...
flw2 发表于 2011-03-18 10:10



    不好意思,晚上加班,现在才回到家。

   也许大家的思维习惯有所不同,你可能比较喜欢直截了当,而我喜欢从内在联系阐述问题,因此我没有直接说C参考手册是错的,而是从背后的原理入手。

   看得出来,你还有些许疑问,那么,这次我不谈直接的原理,而是从侧面去证实,消除你最后一点疑虑。

   &a是合法的这连小孩也知道,而&运算符对操作数的要求,标准是这样说的:

   The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

    显然,数组名属于其中an lvalue that designates an object的情况。

   第二个,我从一个权威性仅次于标准的网站http://c-faq.com上引述一段其它专家的答疑(实际上就是对标准的解释):
   
   其中的第六点:Arrays and Pointers的6.7小点是这样说的:

comp.lang.c FAQ list · Question 6.7

Q: How can an array be an lvalue, if you can't assign to it?
--------------------------------------------------------------------------------

A: The term ``lvalue'' doesn't quite mean ``something you can assign to''; a better definition is ``something that has a location (in memory).'' [footnote] The ANSI/ISO C Standard goes on to define a ``modifiable lvalue''; an array is not a modifiable lvalue. See also question 6.5.

References: ISO Sec. 6.2.2.1
Rationale Sec. 3.2.2.1
H&S Sec. 7.1 p. 179

现在,哪本书、哪段代码说的是对还是错,应该了然了吧?

论坛徽章:
0
60 [报告]
发表于 2011-03-18 23:19 |只看该作者
我们的原则分歧就在这里:“一个标识符只能具有一个类型”

  我并不反对抠字眼,我也知道标准 ...
KBTiller 发表于 2011-03-18 10:33



    C/C++标准不是针对谁而写的,既不是专门针对编译器设计者,也不是专门针对程序员,而是为整个C/C++世界订立的语言法律,无论编译器设计者还是C/C++程序员,作为这个世界的一分子,都应该遵守这个法律。

  此外,标准作为一个既定的事实,不应该脱离标准而臆想,除非你就是C/C++标准委员会,有权力去制定标准。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP