技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 编程语言 --> 类型转换-无处不在的陷阱

类型转换-无处不在的陷阱

浏览:2135次  出处信息

今天在论坛上看有laphon同学的一个问题,觉得这个问题很有意思。
原帖地址
问题援引如下:

做一个小程序的时候发现的。代码如下,使用的编译器为DEV-C++ 4.9.9.2。
如果先要求输入a,再要求输入b,那么a的值无论输入多少(少于255)输出都会是0;
反过来,如果先要求输入b,再要求输入a,那么就会正常。
这是为什么呢?请教达人解释。

#include
#include
 
int main(int argc,char **argv)
{
    unsigned char a,b;
    scanf(\"%d\",&a);
    scanf(\"%d\",&b);
    printf(\"a=%d,b=%d\\n\",a,b);
    scanf(\"%d\",&b);
    scanf(\"%d\",&a);
    printf(\"a=%d,b=%d\\n\",a,b);
    system(\"pause\");
}


devcpp用的是gcc编译器,自己用gcc试了一下,果然是这样。为什么出现这种问题呢?开gdb调试,信息如下:

main (argc=1, argv=0xbfbaf3e4) at a.c:7
7           scanf(\"%d\",&a);
(gdb) n
1
8           scanf(\"%d\",&b);
(gdb) p a
$1 = 1 \'\\001\'
(gdb) n
2
9           printf(\"a=%d,b=%d\\n\",a,b);
(gdb) p a
$2 = 0 \'\\0\'
(gdb) p b
$3 = 2 \'\\002\'
(gdb)

从第12行可以看出,在第一次scanf读入b后,a的值被清0了,这样原因就好分析了。用%d读b的时候,实际上把b转型成了int,即scanf(”%d”,&b)实际上等于 int * p = &b; *p = 2 ,因为scanf接受的参数是指针。问题明显了,int4个字节,char1个字节,scanf实际上向内存里写了4个字节,高地址的3个字节为0,把a的1个字节和前面的参数区的2个字节给覆盖了。

如果声明顺序反过来,b在高地址,就不会把a覆盖,覆盖掉的是栈头部的参数区的3个字节。

但是无论那种方法,都是危险的,都会造成数据丢失。gcc 在开启 -Wall 开关后会给出警告

 warning: format ‘%d’ expects type ‘int *, but argument 2 has type ‘char *

(这个warning应该是针对c标准库函数设计的)。

这种转型实际上C++其实是不允许的。比如

char a; int * p = &a;

编译器(g++)会毫不客气的给出一个error:

error: cannot convert ‘char*’ to ‘int*’ in initialization

但是实际上那段“问题代码”仍然可以编译通过。为什么呢?查看了stdio.h,scanf函数的原型如下:

extern int scanf (__const char *__restrict __format, ...)

printf最后一个参数”…”是一个变长参数,传参时实际传了一个void*进去,真正的类型分析和转型动在printf函数定义内的va_arg宏完成。所以编译器在分析这个函数调用时是无法知到真正的参数类型的。而scanf定义部分早被编译成了2进制lib。所以这段代码在c++中也可以编译通过。

另外有人说Intel C编译器没有这个问题,自己试了一下vc也没这个问题,这令我很费解。个人觉得这个问题似乎很难避免,因为这种用变长参数列表传递的参数,参数类型完全由前面的format string决定,可惜搞不到源码,就没法深究了。

建议继续学习:

  1. linux file命令是如何识别文件的类型的    (阅读:3722)
  2. PHP上传文件类型彻底判断方案及PHP+nginx上传大小彻底控制方案    (阅读:3650)
  3. PHP JAVA C上传文件如何准确判断文件类型-mime知识普及    (阅读:3408)
  4. JavaScript性能陷阱    (阅读:3090)
  5. Java陷阱(2010版)    (阅读:3003)
  6. PHP数据类型隐性转换的陷阱    (阅读:2826)
  7. 移动互联网系统架构十大陷阱    (阅读:2575)
  8. 检查 Linux 下线程库的类型    (阅读:2484)
  9. Zend Parameters Parser新增类型描述符介绍    (阅读:2124)
  10. JavaScript 类型浅解    (阅读:2122)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1