帖子原地址:http://bbs.verycd.com/lofiversion/index.php/t415775.html
ycao 2007-1-7, 23:44 PM
几行很弱的语句,为啥结果不是我想像中的样子?……
代码main() {long a; int b,c; scanf("%ld",&a); b=a/10000; c=(a-b*10000)/1000; printf("%d\n",c); }我敲个88888进去,想像中结果是8,为啥出来是74???
billconan 2007-1-8, 01:47 AM
奇怪的问题 我不管用long还是int都可以得到8 环境是vs2005你该不会在dos环境下编c吧。难道是传说中的tc2.0?????? 教育滞后啊,教育滞后啊。
dos是16位的操作系统。明白了吧 在dos下int为16位,最大65536, 8万多早就溢出了
ycao 2007-1-8, 12:44 PM
是在win下编,但是环境应该是传说中的tc2.0,用的是一个叫“Turbo C/C++ for windows集成实验与学习环境共享版”的软件,挺方便。
老师教育的时候我记得是在dos下用tc的,不过那时候在坐飞机,现在是自我教育……所以还是用的tc,这样能和书上对上号。
我是这样理解:c=(a-b*10000)/1000按照优先级应该是先做算术运算再做赋值吧,在做运算的时候不存在溢出不溢出的问题吧?那a是长整型整个结果就是长整型咯?长整型赋给整型溢出的话不就是把4个字节前面两个撇掉吗?如果是结果是8,撇掉不撇掉前面两个字节不都还是8呀?
希望C学的很扎实的达人告诉我我的理解里哪里出了问题。
billconan 2007-1-8, 14:26 PM
谁说的计算的时候不存在溢出的?拉出去打屁股。当b*10000的时候就溢出了 这时候这个算式的类型还是int
当a减去上面的结果的时候才转化成long
运算的时候那个更精确就以哪个变量的类型为准
以下是我的原创分析~~
反汇编的结果~~
0C07:0215 59 POP CX
0C07:0216 59 POP CX
0C07:0217 33D2 XOR DX,DX
0C07:0219 B81027 MOV AX,2710
0C07:021C 52 PUSH DX
0C07:021D 50 PUSH AX
0C07:021E FF76FE PUSH [BP-02]
0C07:0221 FF76FC PUSH [BP-04]
0C07:0224 9AEA1C070C CALL 0C07:1CEA;到这步做的是b=a/10000;
0C07:0229 8BF0 MOV SI,AX
0C07:022B 33D2 XOR DX,DX
0C07:022D B8E803 MOV AX,03E8
0C07:0230 52 PUSH DX
0C07:0231 50 PUSH AX ;将1000压栈
0C07:0232 8B56FE MOV DX,[BP-02]
0C07:0235 8B46FC MOV AX,[BP-04]
0C07:0238 52 PUSH DX
0C07:0239 50 PUSH AX ;将10000压栈
0C07:023A 8BC6 MOV AX,SI
0C07:023C BA1027 MOV DX,2710
0C07:023F F7E2 MUL DX ;执行b*10000
0C07:0241 99 CWD
0C07:0242 5B POP BX
0C07:0243 59 POP CX
0C07:0244 2BD8 SUB BX,AX
0C07:0246 1BCA SBB CX,DX ;执行a-(b*10000)
0C07:0248 51 PUSH CX
0C07:0249 53 PUSH BX
0C07:024A 9AEA1C070C CALL 0C07:1CEA ;执行c=(a-b*10000)/1000;
我们来看一下计算过程中的内存状况
H:\turboc>debug test.exe
-g=0,23c
input:888888
AX=0058 BX=0000 CX=0000 DX=000D SP=FFD6 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=023C NV UP EI PL ZR NA PE NC
0C07:023C BA1027 MOV DX,2710
-t
AX=0058 BX=0000 CX=0000 DX=2710 SP=FFD6 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=023F NV UP EI PL ZR NA PE NC
0C07:023F F7E2 MUL DX
-t
//comment: AX=0058存的是b的值,也就是88;DX存的是10000,做乘法
AX=6D80 BX=0000 CX=0000 DX=000D SP=FFD6 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0241 OV UP EI PL ZR NA PE CY
0C07:0241 99 CWD
-t
//comment: DX与AX内容相乘,并将结果存于AX,由于超过AX存储的最大值,因此用了DX,
//comment: 880000=000D 6D80 (000D)存于DX,(6D80)存于AX
//comment: CWD 该指令的隐含操作数为DX和AX,其功能是用AX的符号位去填充DX
//comment: 即如果AX的最高有效位为0,则DX=0000H,如果AX的最高有效位为1,则DX=FFFFh.
//comment: 由于AX目前的最高位是0,所以DX=0000H
AX=6D80 BX=0000 CX=0000 DX=0000 SP=FFD6 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0242 OV UP EI PL ZR NA PE CY
0C07:0242 5B POP BX
-t
AX=6D80 BX=9038 CX=0000 DX=0000 SP=FFD8 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0243 OV UP EI PL ZR NA PE CY
0C07:0243 59 POP CX
-t
//comment: 取出了888888,也就是a的值
AX=6D80 BX=9038 CX=000D DX=0000 SP=FFDA BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0244 OV UP EI PL ZR NA PE CY
0C07:0244 2BD8 SUB BX,AX
-t
AX=6D80 BX=22B8 CX=000D DX=0000 SP=FFDA BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0246 OV UP EI PL NZ NA PE NC
0C07:0246 1BCA SBB CX,DX
-t
//comment: 分别对高位和低位做减法,由于DX经过CWD的操作变成了0000H
//comment: 因此在SBB CX, DX时,就无法得出正确的结果。
AX=6D80 BX=22B8 CX=000D DX=0000 SP=FFDA BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0248 NV UP EI PL NZ NA PO NC
0C07:0248 51 PUSH CX
-t
AX=6D80 BX=22B8 CX=000D DX=0000 SP=FFD8 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=0249 NV UP EI PL NZ NA PO NC
0C07:0249 53 PUSH BX
-t
//comment: 实际在做 /1000 的操作前,压入内存的是0D22B8,也就是860856
AX=6D80 BX=22B8 CX=000D DX=0000 SP=FFD6 BP=FFE6 SI=0058 DI=0D40
DS=0DE6 ES=0DE6 SS=0DE6 CS=0C07 IP=024A NV UP EI PL NZ NA PO NC
0C07:024A 9AEA1C070C CALL 0C07:1CEA
-g
860
Program terminated normally
//comment: 于是我这里显示的是860
最终产生的值不同跟机器、编译环境等等都有关系。
程序编译:tc2.0;
调试工具:debug;
我使用的原始程序:
#include
main()
{
long a;
int b,c;
printf("input:");
scanf("%ld",&a);
b=a/10000;
/* c=a-b*10000;
c/=1000;
*/
/*
b*=10000;
c=a-b;
c/=1000;
*/
c=(a-b*10000)/1000;
printf("%d\n",c);
}
ps:使用注释掉的两个部分都可以得到正确的解,而跟踪发现,如果去掉CWD指令换之以NOP(空指令),则也可得出正确解
你这俨然变成专业小文章了~~totally unknown啊~~~爬走
[回复]
只要将a和b设置为长整形即可!
修改后代码如下:
#include “stdio.h”
void main()
{long a,b,c;
scanf(“%ld”,&a);
b=a/10000;
c=(a-b*10000)/1000;
printf(“%d\n”,c);
}
问题主要出在c=(a-b*10000)/1000;!
a和b都为整形数据,则a-b*10000的结果溢出,导致最终结果出错。
[回复]
确实,全long是比较正规的做法,写这个的目的也只借这个机会研究一下具体在内存中的操作情况
[回复]
。。。。。。。。。。。。。。我决定放弃做程序员了|||||||
太可怕了= =。。。我宁愿去卖电脑OTZ。。。。。。
[回复]
Good post.
[回复]