不同平台编译器的差异

经过一番辛苦终于发现了一个BUG,该BUG和代码的编写以及编译器的实现有关,具体情况如下:

switch( data_type )
{
case SE_BLOB_TYPE:
.....
pGeometry
->Set( row , col ++ , buffer , min( length , pGeometry->GetColumnLength( col ) );
......
}

  在 Windows 上,执行结果很正常,可是在 UNIX 上,发现获取 LONG RAW 字段的值,总是只有前四个字节是和数据库一致的,而后面的统一是0,最后查明了原因就是因为在一个函数的调用中同时使用了一个变量和他的自增加操作,经过在不同的机器环境上验证,发现不同的编译器之间后在对该语句的理解上有很大的差异,首先使用的验证程序如下:

#include <stdio.h>

int step( int x , int y , int z )
{
return printf( "x=%d y=%d z=%d\n" , x , y , z );
}

int main( int argc , char * argv[ ] )
{
int col = 5;
return step( col , col ++ , col );
}

  在不同的环境中,该程序的输出结果完全不同。

在 Windows 下,使用命令行 cl test.cpp 编译,然后执行 test.exe 输出结果为x=5 y=5 z=5,说明是首先传参数并调用程序,然后再增加变量 col 的值

在 Ubuntu 11.04 下使用 gcc test.cpp 编译,然后执行 ./a.out 输出结果为x=6 y=5 z=6,这个行为就有些奇怪了,后面再分析

在 IBM AIX 5.3 上使用 xlC_r test.cpp 编译,然后执行 ./a.out 输出结果为x=5 y=5 z=6,说明是从左到右传参数,碰到需要增加值就增加,所以最后一个参数就是 6 了。

在 SunOS 5.9 上使用 gcc test.cpp 编译,然后执行 ./a.out 输出结果为x=6 y=5 z=6,这个结果和 Ubuntu 上一样,说明相同的编译器,在不同的平台上其行为还是一致的。

这样就不难理解为什么开始的那段程序在 IBM 机器上为什么不对了,pGeometry->GetColumnLength( col )获取的其实是下一个字段的宽度,而下一个字段只有四个字节的宽度,因此只有前四个字节正确,后面都是无效的内容。

最后再来分析 GCC 的行为,其实通过使用编译工具通过他们产生的汇编代码可以看到他们的行为,例如使用 cl /FAsc test.cpp 和 gcc -S test.cpp

这里不再列出汇编代码,但是可以通过汇编代码看到他们的差异,msc 是首先将变量放到栈里面并调用函数,然后执行增加操作,而 gcc 则是首先将需要增加的变量放到对应的寄存器中,然后增加,最后把其他参数放到寄存器中调用函数,就是说首先保证 col++ 出现位置的参数为原始参数 5 ,其他的 col 参数都是增加后的值,这就引来另外一个疑问,如果有两个参数使用一个变量的增加,是什么行为?如下代码:

#include <stdio.h>

int step2( int x , int y , int z , int m )
{
return printf( "x=%d y=%d z=%d m=%d\n" , x , y , z , m );
}

int main( int argc , char * argv[ ] )
{
int col = 5;
return step2( col , col ++ , col ++ , col );
}

  其输出为 x=7 y=6 z=5 m=7,仔细分析其实和之前的例子是一致的行为,但是和 IBM 的增加顺序相反,是从右向左的。

最后想说的是,不要在一个函数的参数中出现同一个变量的自增加和非自增加操作,否则在不同的环境里,嘿嘿,后果自负......

posted @ 2011-08-24 16:43  王志科  阅读(948)  评论(0编辑  收藏  举报