Chinaunix首页 | 论坛 | 博客
  • 博客访问: 48487
  • 博文数量: 1
  • 博客积分: 66
  • 博客等级: 民兵
  • 技术积分: 180
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-10 23:41
文章分类

全部博文(1)

文章存档

2012年(1)

我的朋友

分类: C/C++

2012-04-17 20:17:02

    最近项目代码需要从mips平台移植到x86平台,这是公司产品第一次采用x86平台。之前项目很紧,所以很多代码都没有考虑移植性问题,因此移植的时候遇到了不少问题。前几天才解决了位序(也叫比特序,与字节序不同)问题,今天又遇到了一个比较隐蔽的C语言问题,在这里记录一下,告诫自己,也告诫各位同行,避免犯这样的错误。至于位序问题,以后应该会再另写一篇文章来说明。
    原本在mips平台上运行良好的代码,移植到了x86平台,结果却不对了,我们仔细分析了代码,没发现什么可疑的地方,而且我之前为了优化那段代码,单独把那段代码抽出来测试过。我抽出来的代码在两个平台里得出的都是一样的结果。我对比了代码,实现的地方没有任何改动,照理说不应该出现这种情况的。不过根据打印出来的值,我注意到了一种情况,在x86平台里的结果值只有16位,但在mips平台里的结果值有32位,并且低16位的值与x86平台下的值一样。最后,我查看了声明该函数的头文件,才发现头文件里函数的声明与C文件里的实现返回值不一致!
    问题可以简化成下面的代码:

点击(此处)折叠或打开

  1. //crc.c
  2. //注意,此处没有包含crc.h这个头文件!
  3. unsigned int get_crc(void)
  4. {
  5.     return 0x12345678;
  6. }

  7. //crc.h
  8. unsigned short get_crc(void);

  9. //main.c
  10. #include <stdio.h>
  11. #include "crc.h"

  12. int main(int argc, char *argv[])
  13. {
  14.     unsigned int crc = get_crc();
  15.     printf("crc:%x\n", crc);
  16.     return 0;
  17. }

    编译执行: gcc -Wall -o test main.c crc.c  //好吧,-Wall也没办法报错
    在x86平台下输出:5678
    我又分别在mips平台和powerpc平台下编译执行了这段代码,同样没警告或者报错。在mips平台下输出:12345678,在powerpc平台下输出:12345678
    在简化的代码里,大家很容易就能看出是get_crc这个函数的声明和定义(实现)不一致导致的问题,但在庞大的项目文件里,可能就没那么容易看出问题所在了。
    我们在写代码的时候,往往只注意函数的实现,对函数的声明重视不足。在Linux平台下,我们喜欢用cscope+ctags+vim来写代码,修改或者浏览代码的时候也喜欢跳到函数定义处,变量声明处,却很少关注函数声明,导致修改代码之后声明和定义不一致的情形。
    这并不只是程序新手才会出现的问题,工作几年的程序员也可能会犯这样的错误,出现问题的这段代码,就是出自一个已经工作了四年的同事之手。
    也正是在这个时候,我才发现,我们之前的代码是有问题的,只是所谓的“得到了正确的结果”。
    我起先认为对于这种情况,是个编译器未定义形为,不同gcc版本对这种情况的处理可能不一样,但我进行了一些测试,发现情况比我想象中的复杂。在powerpc平台,gcc版本是3.3.x,mips平台,gcc版本是4.3.x,在x86平台,有两个版本的编译器,分别为4.1.x(centos),4,6.x(ubuntu)执行情况是mips平台和powerpc平台一样,都是12345678,x86平台下均为5678。mips和powerpc都是大端,x86是小端,至令我没办法判断真正的问题在哪,是编译器版本原因还是与大小端有那么点关系。还望知道的朋友不吝赐教。
    此外,我还测试了对于变量的情况,发现对于变量的处理,各个gcc版本不同平台都是一致的,当然,由于大小端的关系,输出结果会不同。大家有兴趣可以试一下。
    说了那么多,只是想说明这个隐蔽的错误大家一不小心就很容易犯,而且后果也比较严重,得找到方法避免。
    解决办法很简单,那就是通过把函数声明(原型)放在头文件中,而函数定义则放在另一个包含了该头文件的源文件中。这样编译器就能发现不一致的情况从而报错提醒我们。这个问题在《C专家编程》8.5节有论述。
    我单独提出来的代码之所以结果一致,是因为我把函数定义跟对该函数的引用都放一个文件中了,没有使用头文件。

    4月18日补充:由于之前mips平台和x86平台gcc版本不一样,没有可比性,今天到公司换了一样的gcc版本进行测试,发现mips平台下还是输出12345678,这看来应该是编译器后端的行为,有时间看下mips汇编确认一下吧。
阅读(3793) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~