C语言十分令人不解的...?(100分)

  • 主题发起人 主题发起人 张利中
  • 开始时间 开始时间
to sw:
>>编译器“认为”等号右边是一个表达式(记住:是一个表达式),然后将这个表达式分解,
>>先将乘号左右的部分算出来,再将每一部分乘起来,这样步骤如下:
...
你说得很“对”呀!如果“先将乘号左右的部分算出来,再将每一部分乘起来”
那么左边部分算出来等于6,右边部分算出来等于5,再乘起来应该等于30呀??
你的表达就是我说的正常人的逻辑思维方式,可是与结果不相符啊!

>>绝大部分的系统软件都是用C/C++开发的,这些人不会个个都是傻瓜吧!!!!
又来这种陈词滥调了... 技术问题归技术问题,不要动不动东拉西扯。
我说C语言不怎么样,起码还没忘了在后面加上一个笑脸,而不是4个叹号,
只要思想不是太偏激的人,肯定都能理解我的意思,你要是看不惯,不理睬就是了,不用这么大动静吧!
 
大家都是同行,不用这么大反应吧!
 
c语言本没有错,这看你如何使用了,从软件工程的角度来说,一个好的程序应该是别人容易看懂,
且不会产生歧义.
 
to 温柔一刀:
我无意和某个具体的人作对,只不过是看到"只能说一句 C语言确实不怎么样!","这根本不是Borland
还是M$的问题,这是C语言的问题。"这两句话太受不了了,我想每个钟爱C/C++的人都会受不了的.所以我
才比较激动吧!

若i=3, k = (++i) + (++i) + (++i);
>>有人以为从左到右使i增值,相当于k=4+5+6,得15.事实上k=18.原因是:++i的自加是在整个表达式求解一
>>开始时最先进行的,既对表达式扫描,先对i进行三次自加,i得6,然后进行k=6+6+6的运算,故得18.

上面的>>部分引自谭浩强的《C程序设计》1991年7月第一版的第25页的第8到第10行.

这样看来应该这么说:既不能说人的思维有问题,也不能说编译器有问题,而只能说那些不明白的人没有
搞清楚编译器在处理这个问题时是怎么工作的.

对于第二种情况printf("%d", ++x*--x); 我还是坚持自己的观点,就是Borland的编译器没有把它看做
是一个表达式,而是两个(我一再声明:或许不应该说是一个或几个表达式,但到底应该怎么措辞,我搞不大
清楚,就是这么个意思.我想大家也能理解吧).或许VC++和Borland的编译器在这里处理的方式不同吧,谁都
没错!

我刚才去查了一下谭浩强的电话和E_Mail,可没找到 :-(
总有一天,我要向他请教一下!!!!

to 教父:
我的解释能让你满意吧!别忘了你说的话,给我加分吧,呵呵:-)
(开个玩笑)
 
答案在此:
1.
mov word ptr [bp-2],5
inc word ptr [bp-2]
dec word ptr [bp-2]
mov ax,word ptr [bp-2]
imul word ptr [bp-2]
mov word ptr [bp-4],ax
push word ptr [bp-4]
push offset DGROUP:s@
call near ptr _printf
2.
mov word ptr [bp-2],5
inc word ptr [bp-2]
mov ax,word ptr [bp-2]
dec word ptr [bp-2]
mov dx,word ptr [bp-2]
imul dx
push ax
push offset DGROUP:s@
call near ptr _printf
C++ 编译器在处理普通表达式与传入参数表达式是不同的。
 
差点忘了说:这个极端的例子你都想得出来,I 服了 U!
C / C++ 是几近完美的语言,这只是完美中的一点小小缺憾。VC 虽然在这道题上没出问题,
但是 M$ 对 C++ 语义的解释……我吐,吐,吐……太……差……了…………
 
to mikedeakins:
哈哈!你真有意思!我同意你的看法。
不过...... >>这只是完美中的一点小小缺憾
这是什么意思?这怎么能是缺憾呢?!如果有人问你:C = C + 1 和 C++ 哪个效率高时,
你怎么回答?毫无疑问!! C++ !!!!
 
C 表达式本来就有一部分语义 ANSI 没有作规定,这依赖于各个软件开发商自己的
理解以及实现(大多数 C 书都有描述)。如果非要使用这一部分语义,必须在不同
的平台上分别作测试。
题里面的表达式本来就模棱两可,具体要实现什么功能我也说不清楚。而且,如果你
研究过编译器就会知道:普通表达式返回值需要占用一个存储单元,而出现在函数参
数表的表达式不需要,因为可以将寄存器中的临时值直接 PUSH 到堆栈里。
如果语义比较复杂,建议还是使用 C + 1 的表示方法,八百年前的 C / C++ 编译器
就可以在 C + 1 的时候使用 INC 指令代替了。还有什么 * 2, / 2 ,有人如获至宝
似的用 << 2, >> 2 代替,做 C / C++ 编译器的兄弟们又不是白痴……
 
to sw、mikedeakins :
我说话当然要算数啦,两位请到http://www.delphibbs.com/delphibbs/dispq.asp?LID=484987
去领分吧 :)
 
>>或许VC++和Borland的编译器在这里处理的方式不同吧,谁都没错!
你说得太对了,谁都没错,可是这样就奇怪了!
编译器没错,编程序的人没错,最终得到的程序运行结果在不同编译器上是不同的,
那么程序还有什么正确性可言呢?因此确实只有一句话:这是C语言的错--
他的语法机制造成了这种不一致的可能,难道我说的不对么?

如果象mikedeakins说的,“这个极端的例子你都想得出来”,那就更“错”了 :-)
咱们的谭大师不是早就举了这样的例子了么?而且还是在书上教别人理解这种“极端的例子”,
足见这并非什么极端例子,而是C语言固有的特点。其实这种特点在C语言中还多得是,
需要人处处小心,每一个学习过的人都知道。这样的语言,竟然称之为近乎完美...

唉!批评两句C/C++,就有人跳着脚出来辩论,难怪这世界上...
 
to 温柔一刀:
哈哈!你这个人啊!

>>你说得太对了,谁都没错,可是这样就奇怪了!
>>编译器没错,编程序的人没错,最终得到的程序运行结果在不同编译器上是不同的,
>>那么程序还有什么正确性可言呢?因此确实只有一句话:这是C语言的错--
>>他的语法机制造成了这种不一致的可能,难道我说的不对么?
在Borland C++和VC++中都有求某一天是星期几的函数,前者认为返回0代表星期一,而后者认为0
是星期日。你能说哪个是对的,哪个是错的呢?当然都是对的!

我觉得本人看问题还是比较公正的!
我说C/C++语言好,但从来不否认它也有缺点、不足:许多专家认为C++由于是从C语言继承过来的,
使得它的面向对象不是那么标准;又如:C++语言之所以效率高、速度快,是因为它是编译型语言,采
用的是静态联编,而纯粹的面向对象语言由于其执行机制是消息传递,所以只能采用动态联编,不得已,
后有加入了动态联编,给人一种牵强的感觉...... 但瑕不掩瑜,它的众多优点使得C/C++在当今流行
编程语言中的霸主地位无人能撼动。我还是那句话,即便日后C/C++被别的什么新型语言所替代,可以
肯定地说,那种新型语言借鉴的最多的一定是C/C++!
再有,我说C/C++语言好,但从来没说其他语言不好!每一种编程语言、编程工具之所以能有其用户群,
都说明它总有一些其他语言不能替代的优点。正如:Internet的出现并没有把传统媒体挤垮,说明那些媒
体还有其不可取代的优点。
 
你越说越离谱了,函数和表达式/编译器是一个层次的概念么?
函数的返回值及其解释可以由程序和人来决定,但是一个符号序列是否是表达式,
语言当中应该是有严格规定的,岂是你认为/编译器认为就可以的??

你还是好好从头把这个帖子看一遍吧,你在这个问题上并没有提出任何新观点,
关于表达式的问题,也是我先提出的。而你的解释,恰恰是我说的“不太可信”的那一种。
换句话说,在25与30的这个问题上,我宁愿相信Borland编译器在这个地方处理“错误”
(因为VC并没有这样处理,而Borland编译器在其他地方的处理与VC一致),
也不愿意认为是C语言中对于什么式表达是这个概念没有明确的定义,否则岂不是进一步证明了C语言之“差”?
(一个连什么式表达式都没有明确规定的语言,我简直不敢想象...)
连我这样一个说“C语言不怎么样”的人都不愿意相信的事情,竟然来自于你为C语言的“辩护”,实在有让人忍俊不禁的感觉... :-))

至于你后面说的,什么优势/劣势云云,可以说我没有一点不同意见,完全赞同,这也是我一向的观点。
然而有意思的是,你既然有这么客观的观点,却不去正视这明显的C语言的缺欠之处,反而为其进行辩护。
究其原因,大约是因为我的一句“不怎么样”而导致感情难以接受?
因此,这才是当今人们的一大通病,很显然你也没有脱离这个怪圈 -- 爱屋及乌... ^_^
 
我之所以提出上面的问题,是因为,有如下一道题:
选择题:
如果int x=5; 则++x*--x的执行结果是:()
A. 25 B. 30 C. 20
答案给的是A
有人上机用前面的两种方式调试,在turbo c 下得到25,30两个结果
在VC下只有一种结果25
十分感谢各位朋友的大力指教,特别是mikedeakins的汇编语句解释得清晰明了,
在此,特向他(她)表示诚挚的谢意。另外,还想请问:mikedeakins,
那段汇编是如何得到的?VC环境可以完成C/C++语句到汇编语句的转换,
那么,在Turbo c 下可不可以实现这个功能呢?
 
1. main()
{ int x=5;
int y;
y=++x*--x;
printf("%d", y);
}
2. main()
{ int x=5;
printf("%d", ++x*--x);
}
上机运行,第一个题输出25, 第二个题输出30 .不明白为什么?
请各位指教!

以上程序在TURBO 2。0 中有个优先级的问题,为了避免这种问题最好加()
y=(++x)*(--x);
printf("%d",(++x)*(--x);
OK!
在c++中没有这种问题。
 
Turbo C 我不知道,Borland C++ 3.1 是可以的。在 Compiler 设置里面
选择 Generate ASM Code,(然后可以选择 Compile via assembler)。
我贴的汇编代码就是 BC++ 31 生成的。
 
从编译的情况看
1.种情况里面++x用的指令是inc,--x用的是dec对同一地址进行操作,然后再对这个地址相乘
2.种情况则是自加后就把第一操作数移入寄存器,再累加,并把第二操作数移入寄存器mul
我觉得这是编译器的优化代码而产生的问题,对同样的操作进行了不同的编译,正如sw所述,是因为'='的原因,
,C语言是没有问题的,因为此句在C中必没有任何歧义
不管是怎么样都应该当作
a=x+1;
b=x-1;
y=a*b;
而不应当作 x=x+1;
x=x-1;
y=x*x;来算,编译器在这里优化了一下,想节省空间
第二种优化显然要合理得多,并没有申请中间变量而多占空间,用来了寄存器来存储中间变量,
速度可能也要比第一种快,原因是 mov dx,word ptr [bp-2]
imul dx两条指令加起来的
速度也应比imul word prt [bp-2]一条指令快

我想这应该算作是各编译器的一个bug.
 
哈哈哈哈哈哈哈....我不由得放声大笑...

如果我还没有老糊涂的话,那么sonie的这段话:
"C语言是没有问题的,因为此句在C中必没有任何歧义
不管是怎么样都应该当作
a=x+1;
b=x-1;
y=a*b;
而不应当作 x=x+1;
x=x-1;
y=x*x;来算,"
的意思是说正确答案应该是30,而不是25,对么????

首先,不管事实如何,只要我上面的理解是对的,那么sonie同志能够说出这样的话(尤其是在前面已经有谭XX的书作证据的情况下),
足以证明我说的“思维正常的人会认为30是标准答案,而25则是C语言副作用发挥到极点的充分证明”并非仅仅一面之词。

其次,问题恰恰出在这里,按照可以理解的结果来看,y:=++x*--x;
恰恰被认为是x=x+1;x=x-1;y=x*x -- 不要忘了,四种情况下,三个答案都是25,
而且这还被谭XX用来作例子,教人们如何理解C语言表达式的计算。
因此,如果这些编译器不至于都错,那么这就是C语言的副作用的一个极好证明。

当然,也确实不能排除“所有的编译器此处都错了”这种可能性(逻辑上任何事情都有可能,虽然我不太相信)...
但是不管怎么样,感谢sonie“现身说法”,敢于和我一样,说明25这个结果可不是那么符合正常人的逻辑 :-))))))))
 
还有个怪程序
据说是一道中科院的考研题

程序如下
int fac() {
static int i=5;
if(i==0) return(1);
else { i=i-1; return(( i+abs(1))*fac()); }
}

main(){
printf("factor of 5=%d/n",fac());
}

程序得到的结果
在tc2.0下的结果为120,
在gcc和c++ builder下的结果是1,
vc更奇怪,debug版是120,release版是1

原因好像是 编译器对(i+abs(1))*fac())运算的优先级定义不同
 
这个帖字网易也讨论过,
类似这样有争议的写法不应该提倡。
最早弄出这写法的思维也有问题。
 
to 温柔一刀:的确我会认为30是正确答案,但我的另一个意思正好是编译器有问题而不是c的原因
以上纯属一家之言,所以说,学术上有很多流派就是这样来的,
不见得谭xx说什么什么就对,各人有各人的理解。
to GuXl:我认为此类问题的根源在于编译器没有统一的编译优化标准。
如果有一个标准大家都按章办事的话,那么不管是理论还是实践,这道题目应该没有歧义.
其实上面一题的问题所在主要在于编译时mov ax,_x的位置,
在x++,x--之前之后就结果大不一样.
 

Similar threads

D
回复
0
查看
1K
DelphiTeacher的专栏
D
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
670
import
I
后退
顶部