Windows与Linux编译器的差异

  移植工作开始后的第一步就是在目标平台Linux上进行编译,并链接源代码。由于需要移植的软件通常并未在Linux平台上编译过,编译的过程可能会遇到很大的困难。一般情况下,由类型声明引起的编译错误是比较容易修复的。比如Microsoft C/C++的头文件使用__declspec( dllimport/dllexport )来输入和输出DLL函数,在Linux上,把函数声明成extern C”,或者再结合使用DEF文件,使用相应的链接命令就可以解决这些问题。但困难的地方在于编译器之间存在差异的部分,同时这也是可能引起很多运行时问题的重要因素,读者有必要在开始移植之前就充分了解。在此讲述一些容易被忽略而且后果比较严重的方面。

Visual C++ 2003GCC 4.1.0 为例。前者是Windows平台的主流编译器,兼容性良好,但是对C++标准的遵循并不严格。这意味着即使开发者写出不太符合标准的程序,编译器也可能能容忍。相反的是,GCC对标准的遵循相对严格得多,这样很容易造成在Windows运行良好的程序,在Linux上却引起意想不到的编译甚至运行时错误。

1)基本类型大小和结构对齐
 
首先是 C/C++语言基本类型的大小,以及相应的结构对齐问题。典型的例子是long关键字。在Visual C++ 2003下,sizeof(long double)8,其大小和double一致。但是在GCC 4.1.0上,sizeof(long double)等于12,其大小比double4。另一个和大小相关的问题是对齐问题。不同编译器的默认对齐大小是不一样的。一般情况下程序逻辑都跟对齐无关,但是涉及从磁盘或者网络文件中读取结构时(如解析资源),精确的对齐就是必需的。考察下面的程序段:

#include <stdio.h>
struct A

{
         char a;
         double b;
};

int main()
{
    printf("%d %d %d/n", sizeof(long double),
      sizeof(long long), sizeof(A) );
    return 0;
}

 
上面这段程序在 Visual C++ 2003编译器默认设置下,输出结果为8 8 16;在GCC 4.1.0编译器默认设置下,其输出为12 8 12。从sizeof(A)的大小可以看出,Visual C++ 2003是按8字节对齐的,而gcc4字节对齐的。这时需要使用#pragma pack预编译指令来修改头文件中的结构声明,或者在运行时调整内存中结构成员的位置。不管采用何种方法,对齐都是需要小心处理的事情。

 
一个引起最大麻烦的基本类型是wchar_t。在Visual C/C++ 2003编译器中,wchar_t的大小是2字节,并且可以和unsigned short类型互相赋值。与此关联的一系列Unicode相关函数,比如wcslen,wcscmp等,都接受UTF16格式的Unicode串。在 GCC中,其大小是32位。与此相关的wcslen,wcscmp函数都接受UTF32格式的Unicode串。为此,必须在Linux上开发一套 UTF16接口的wcs系列函数,以保证UTF16的字符串被正确处理。与此同时,使用宏定义来替换wchar_t关键字为unsigned short,以保证函数声明的兼容。

2new操作符的出错处理
 
另一个问题是new操作符的出错处理。由于编译器的设置不同,new操作符可能具有不同的行为。考察如下的代码段:
#include <stdio.h>
class A
{
public:
    void *operator new( size_t size )
    {
         return NULL;
    }
    A()
    {
         printf("Constructor called/n");
         a = 0;
    }
private:
    int a;
};

int main()
{
    A *p = new A();
    printf("%x/n", p );
    return 0;
}

 
Visual C++ 2003中,上面的程序输出0。而GCC 4.1.0编译器的输出结果为:
Constructor called
Segmentation fault

 
也就是说,Visual C++ 2003的编译器会检查new的返回值,如果返回为空,构造函数就不再执行。但是gcc必须加上–fcheck-new编译参数才具有这一行为:g++ fcheck-new test.cpp。这样在Linux上上述程序也会输出0

3)结构化异常和C++异常
 
还有一个更隐蔽的差异存在于异常处理。Visual C++并不遵循异常处理的C++规范。考察如下的程序段:
#include <stdio.h>
int main()
{
    int* p = NULL;
    try
    {
         *p = 0;
    }
    catch (...)
    {
         printf("caught the exception/n");
         return 1;
    }
    return 0;
}

 
读者可以自己用 Visual C++ 2003GCC分别检验这段程序。前者生成的程序在Windows上正常运行,输出caught the exception,然后正常退出。而GCC生成的程序只是输出Segmentation fault。所以在Windows上,catch语句抓住了一个异常。按照C++的标准,只有使用throw语句,才能产生异常。但是在上面的程序段中, 只是一个简单的赋值语句。原因在于,Visual C++ 2003C++的异常处理映射成了Windows的结构化异常处理。在上面的语句中,*p = 0将引起一个Windows的异常,Visual C++将它处理成一个C++异常,并进入catch块。在Linux上,由于没有C++异常发生,程序直接崩溃。

你可能感兴趣的:(C++,windows,linux,gcc,Constructor,编译器)