Linux與Windows編譯器的區別


移植工作開始后的第一步就是在目標平台Linux上進行編譯,並鏈接源碼。因為須要移植的軟件通常並未在Linux平台上編譯過,編譯的過程可能會遇到非常大的困難。普通情況下,由類型聲明引起的編譯錯誤是比較easy修復的。比方Microsoft C/C++的頭文件使用__declspec( dllimport/dllexport )來輸入和輸出DLL函數,在Linux上,把函數聲明成extern “C”,或者再結合使用DEF文件,使用對應的鏈接命令就能夠解決這些問題。但困難的地方在於編譯器之間存在差異的部分,同一時候這也是可能引起非常多執行時問題的重要因素,讀者有必要在開始移植之前就充分了解。在此講述一些easy被忽略而且后果比較嚴重的方面。

  以Visual C++ 2003和GCC 4.1.0為例。前者是Windows平台的主流編譯器,兼容性良好,可是對C++標准的遵循並不嚴格。這意味着即使開發人員寫出不太符合標准的程序,編譯器也可能能容忍。相反的是,GCC對標准的遵循相對嚴格得多,這樣非常easy造成在Windows執行良好的程序,在Linux上卻引起意想不到的編譯甚至執行時錯誤。

(1)基本類型大小和結構對齊

  首先是 C/C++語言基本類型的大小,以及對應的結構對齊問題。典型的樣例是longkeyword。在Visual C++ 2003下,sizeof(long double)是8,其大小和double一致。可是在GCC 4.1.0上,sizeof(long double)等於12,其大小比double多4。還有一個和大小相關的問題是對齊問題。不同編譯器的默認對齊大小是不一樣的。普通情況下程序邏輯都跟對齊無關,可是涉及從磁盤或者網絡文件里讀取結構時(如解析資源),精確的對齊就是必需的。考察以下的程序段:

#include 
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字節對齊的,而gcc是4字節對齊的。這時須要使用#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_tkeyword為unsigned short,以保證函數聲明的兼容。

(2)new操作符的出錯處理

  還有一個問題是new操作符的出錯處理。因為編譯器的設置不同,new操作符可能具有不同的行為。考察例如以下的代碼段:

#include 
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 
int main()
{
int* p = NULL;
try
{
*p = 0;
}
catch (...)
{
printf("caught the exception\n");
return 1;
}
return 0;
}

  讀者能夠自己用 Visual C++ 2003和GCC分別檢驗這段程序。前者生成的程序在Windows上正常執行,輸出caught the exception,然后正常退出。而GCC生成的程序僅僅是輸出Segmentation fault。所以在Windows上,catch語句抓住了一個異常。依照C++的標准,唯獨使用throw語句,才干產生異常。可是在上面的程序段中, 僅僅是一個簡單的賦值語句。原因在於,Visual C++ 2003將C++的異常處理映射成了Windows的結構化異常處理。在上面的語句中,*p = 0將引起一個Windows的異常,Visual C++將它處理成一個C++異常,並進入catch塊。在Linux上,因為沒有C++異常發生,程序直接崩潰。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM