某论坛的人用tc2.0引出的问题的研究

帖子原地址: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(空指令),则也可得出正确解

《某论坛的人用tc2.0引出的问题的研究》有5个想法

  1. 只要将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的结果溢出,导致最终结果出错。

    [回复]

  2. 确实,全long是比较正规的做法,写这个的目的也只借这个机会研究一下具体在内存中的操作情况

    [回复]

  3. 。。。。。。。。。。。。。。我决定放弃做程序员了|||||||

    太可怕了= =。。。我宁愿去卖电脑OTZ。。。。。。

    [回复]

发表评论

电子邮件地址不会被公开。 必填项已用*标注