IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

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

Programming Life with Music 2009-11-17 23:15:54 累计浏览 3,325 次
本机暂存

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

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


devcpp用的是gcc编译器,自己用gcc试了一下,果然是这样。为什么出现这种问题呢?开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应该是针对c标准库函数设计的)。

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

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

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

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

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

同分类推荐文章

  1. 科技爱好者周刊(第 401 期):如何赚到10亿美元 (2026-06-26 08:05:38)
  2. 如何做决策 - 从 Go 的一个 issue 说起 (2026-06-26 08:00:00)
  3. Seven Player:Windows上播放115网盘视频的增强工具 (2026-06-09 00:06:47)

查看更多 开发者 文章 →

建议继续学习

  1. 如何学好C++语言 (累计阅读 10,449)
  2. Emacs配置C/C++-mode的代码智能提示和自动补全 (累计阅读 10,412)
  3. STRUTS2类型转换错误导致OGNL表达式注入漏洞分析 (累计阅读 10,290)
  4. colortail,让 tail 命令绚丽起来 (累计阅读 10,260)
  5. 在C++中实现foreach循环,比for_each更简洁! (累计阅读 9,500)
  6. 几个内存相关面试题(c/c++) (累计阅读 9,447)
  7. 关于使用STL的红黑树map还是hashmap的问题 (累计阅读 8,876)
  8. 浅析C++多线程内存模型 (累计阅读 8,803)
  9. C++ 多线程编程总结 (累计阅读 8,098)
  10. 使用gdb调试运行时的程序小技巧 (累计阅读 7,209)