首页
社区
课程
招聘
[讨论]有趣的C程序,欢迎踊跃讨论
发表于: 2009-12-6 13:28 12019

[讨论]有趣的C程序,欢迎踊跃讨论

2009-12-6 13:28
12019
首先,VC下的,一个很简单的程序

# include <stdio.h>
int main ()
{
  short int a ;
  a = 0x8000 ;
  if (a == 0x8000)
    printf ("T\n") ;
  else
    printf ("F\n") ;
  return 0 ;
}


大家猜才结果是什么呢?
答案是F。

欢迎提出自己的观点认为出现这个问题的原因在哪,下面是冬祭的解释,个人认为不太合适,所以搬来看雪 一起讨论。
http://bbs.pediy.com/showthread.php?t=95528 的观点:
[QUOTE=冬祭;669661]
如果你认为是T,呵呵,最好自己调试一下,找到原因,不太难的,找不到的话再往下看~~
(如果你认为本来就是F,理由是short int 的范围是-32768到32767,这里0x8000是32768,溢出了,不相等,那么,不好意思,你的理解有偏差。不信你把short int换成int,再把0x8000扩展到0x80000000,同样的问题在int型的时候输出T,怎么解释?~)
这里,a是short int型,占两个字节。当我们把0x8000赋给a后,a中的值为
1000 0000 0000 0000(二进制)
在执行if条件时,程序要把a的值和立即数0x8000进行比较。
但是a的值保存在内存当中,于是程序会将a的值从内存里取到寄存器中(通常是eax)
mov eax, a的地址   ;    这个是假想生成的代码
但是a的值只有2字节,eax有4字节。这里编译器在取的时候做了一个符号扩展:
movsx eax, a的地址    ;  这个是实际生成的代码
这样,编译器其实是拿eax里,也就是a的值符号扩展后,也就是0xffff8000和0x8000进行比较,判定为不相等。
现在,如果把short int 换成int,0x8000换成0x80000000就不会出现判断失误的问题了。因为int和eax是等长的,没有进行符号扩展。
[/QUOTE]

个人观点:

因为如果把 if (a == 0x8000) 这句话修改为 if (a == (short int)0x8000) 那么输入结果就变为了T,所以问题的所在是编译器对这个立即数 0x800的解释是什么样的,在本程序中,通过反汇编发现
没有(short int)
11:     if (a == 0x8000)
0040102E   movsx       eax,word ptr [ebp-4]
00401032   cmp         eax,8000h
00401037   jne         main+38h (00401048)
加上了 (short int)
11:     if (a == (short int)0x8000)
0040102E   movsx       eax,word ptr [ebp-4]
00401032   cmp         eax,0FFFF8000h
00401037   jne         main+38h (00401048)

不加(short int) 对立即数0x8000的解释是0x00008000 ,而加上 (short int)之后就会解释成 0xFFFF8000(应该是一个符合扩展,将0x8000扩展成 0xFFFF8000),所以本题的应该注意的另一个问题是对立即数解析方式。
提一个问题,在设计编译器的时候,像本例子中 short int a需要符号扩展成4字节然后再比较,为什么不把立即数也扩展成相同字字节数(4字节)来比较呢? 我认为出现这个问题的关键在微软没有将编译器设计好 哈哈

编译器改成这样:
movsx       eax,word ptr [ebp-4]
mov          bx,0x8000h
movsx      ecx,bx
cmp          eax,ecx
jne         main+38h (00401048)

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (43)
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
2
C我也遇到很多问题
比如内联汇编中
int i,i2
__asm mov i,i2
编译不过去
还非得这样写
__asm mov eax,i2
mov  i,eax
真郁闷
2009-12-6 13:33
0
雪    币: 2538
活跃值: (1344)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
字节不对应的问题么?

微软不也在更新编辑器么
2009-12-6 13:50
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
4
Intel 汇编语言程序设计 第4章 数据传送,寻址和算术运算
4.1.4 MOV指令 中有一段话
MOV指令对操作数的使用是非常灵活的,只要遵循以下的规则即可
1.两个操作数的尺寸必须一致
2.两个操作数不能同时为内存操作数
3.目的操作数不能是CS,EIP和IP
4.立即数不能直接送段寄存器


你说的问题是第二条提到的
2009-12-6 13:52
0
雪    币: 245
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
mov dword ptr [ebp+??], dword ptr [ebp+??]

你能告诉我这是哪种寻址方式吗?
2009-12-6 13:53
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
唉,汇编没学合格,闹出笑话了
2009-12-6 14:12
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
7
如果有mov dword ptr [ebp+??], dword ptr [ebp+??]这样的寻址方式就好了
2009-12-6 14:14
0
雪    币: 272
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
......你两个操作数都是指针,CPU晕了........
2009-12-6 14:21
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
9
那要实现i=i2的值,得非要用寄存器么?
2009-12-6 14:24
0
雪    币: 411
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
不支持内存到内存的寻址。还有short int最大15位~也就是0x7fff
2009-12-6 14:30
0
雪    币: 18
活跃值: (80)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
bug
2009-12-6 14:39
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
12
还有我现在是对mov和lea给搞晕了
2009-12-6 14:44
0
雪    币: 212
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
vs 2008直接出来警告告诉你赋值不对
tt.cpp(5) : warning C4309: '=' : truncation of constant value

'conversion' : truncation of constant value

The type conversion causes a constant to exceed the space allocated for it. You may need to use a larger type for the constant.

The following sample generates C4309:

// C4309.cpp
// compile with: /W2
int main()
{
   char c = 128;   // C4309
}
2009-12-6 15:35
0
雪    币: 1490
活跃值: (1233)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
14
学习了....
2009-12-6 15:43
0
雪    币: 190
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
定义short int a=0x8000, 实际上是1个负数。

如果拿a 和 0x8000做比较,if条件中的0x8000是32位int型的,C语言在不同类型比较的时候会自动做类型转换。所以比较时会将a转换为int型的。显然不相等。

如果定义a为unsigned short int a = 0x8000.结果就是T
2009-12-6 15:49
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
16
short int 型的 0x8000是一个负数
二进制是 1000 0000 0000 0000
是10进制的-32768即最小的short int型的负数
2009-12-6 16:29
0
雪    币: 51
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
对应C语法来说:一个是传值,一个是传地址;一个是变量,一个是指针变量。
2009-12-7 17:11
0
雪    币: 50161
活跃值: (20615)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
18
编辑了一下你的帖,直接告诉大家冬祭原帖没关系的,和冬祭沟通过了。大家互相讨论才能进步。
标 题: 【原创】一个C里容易忽略的细节
作 者: 冬祭
时 间: 2009-08-11,20:21
链 接: http://bbs.pediy.com/showthread.php?t=95528
2009-12-7 21:33
0
雪    币: 91
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
噢,原来这就是溢出啊!
2009-12-7 22:28
0
雪    币: 157
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
20
楼上,这个不算溢出,就算是也是假的溢出。
另外楼主对我曾经发的问题进行讨论,很高兴。不过如kanxue老大所引用,原文中的“某论坛”可能是楼主疏忽了,这个论坛正是看雪。有点哭笑不得的感觉。

关于这个问题,我已经找到了理论依据。
K&R的《The C programming language》中提到的整型提升(integral promotion)的概念如下:
"A character, a short integer, or an integer bit-field, all either signed or not, or an object of enumeration type, may be used in an expression wherever an integer maybe used. If an int can represent all the values of the original type, then the value is converted to int; otherwise the value is converted to unsigned int. This process is called integral promotion."
此处正是这个语法点。并不是微软的编译器弄错了。
2009-12-7 23:36
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
21
囧~~~不是啦,我看的这个帖子真不是在看雪看到的,是在一个讨论MFC的论坛看的,他们转贴也没说原帖在哪    在看雪看到会说啦。。。 哈哈

感谢冬祭,知道了一个整形提升的概念,还有一个,比较小的立即数(int的范围内)汇编里会当成int型来处理,不管这个立即数多小, 人家微软没错,哈哈~~
2009-12-8 08:56
0
雪    币: 157
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22
囧rz。网上很多帖子、教程其实都是出自看雪的。。。
昨天写了一个测试程序:

# include <stdio.h>

int main (void)
{
    unsigned char a = 0x80 ;

    if ( a == (char)0x80 )

        printf ("T") ;

    else

        printf ("F") ;

    getchar () ;
    return 0 ;
}

TC和VC得到不同的结果。这个比较有意思……也许是因为TC是16位的,VC是32位的?~
2009-12-8 10:22
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
23
你要是把这句   if ( a == (char)0x80 ) 改成这样    if ( a == (unsigned char)0x80 )
结果就应该是一致的啦  (char)0x80 做的有符合扩展, (unsigned char)0x80 是无符号扩展,导致结果就不一样
2009-12-8 12:16
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
24
不带unsigned
11:       if ( a == (char)0x80 )
0040102C   mov         eax,dword ptr [ebp-4]
0040102F   and         eax,0FFh
00401034   cmp         eax,80h
00401037   jne         main+38h (00401048)
12:
13:           printf ("T") ;
00401039   push        offset string "T" (00423020)
0040103E   call        printf (00401410)
00401043   add         esp,4
14:
15:       else
00401046   jmp         main+45h (00401055)
16:
17:           printf ("F") ;
00401048   push        offset string "F" (0042301c)
0040104D   call        printf (00401410)
00401052   add         esp,4
------------------------------分割线-------------------------------------------------
带 unsigned
11:       if ( a == (unsigned char)0x80 )
0040102C   mov         eax,dword ptr [ebp-4]
0040102F   and         eax,0FFh
00401034   cmp         eax,80h
00401039   jne         main+3Ah (0040104a)
12:
13:           printf ("T") ;
0040103B   push        offset string "T" (00423020)
00401040   call        printf (00401410)
00401045   add         esp,4
14:
15:       else
00401048   jmp         main+47h (00401057)
16:
17:           printf ("F") ;
0040104A   push        offset string "F" (0042301c)
0040104F   call        printf (00401410)
00401054   add         esp,4
看完这2段反汇编,我彻底晕了  谁来指导指导。。。冬祭大侠?
2009-12-8 12:29
0
雪    币: 2368
活跃值: (81)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
25
unsigned
有符号和无符号的,需要关注的是最高位。是0还是1 是1代表负是0代表正
比如 char 大小由-128到127 你的0x80实际上是-128
而unsigned char 为0到255 如果你比较的时候都是在大于等于0小于等于127 的范围内使用。两者是一致的。
立即数默认为四字节有符号的整数。0x80相当于 0x00000080.
2009-12-8 13:13
0
游客
登录 | 注册 方可回帖
返回
//