C++ 如何跨平台判斷操作系統是32位還是64位?

1、判斷操作系統位數,不是判斷編譯器,也不是判斷cpu;

2、不調用操作系統API,要求跨平台判斷。

3、如果可以順便解釋下原理,以及為什麼;

自己通過sizeof判斷,不準確,估計是跟編譯器有關。


判斷來幹嘛。你一個32位程序,運行在64位的Windows裡面,API就欺騙你了,讓你覺得周圍的一切全部都是32位的。你直接去讀CPU的型號和Windows的版本信息吧。

說到版本的問題,根據微軟的尿性,每一個版本的Windows都有一個全新的閱讀Windows版本的方法,也就是說,這相當於你只能辨別你寫程序的時候已經存在的Windows版本,同時也相當於更高版本的Windows都會欺騙你說它是你當初知道的最新的那個版本。


這裡提供另一種思路,來自於cmake。

cmake在檢測編譯器的時候,用了一種很暴力的方法。可以在不運行實際代碼的情況下直接知道目標平台的信息。做法是這樣的。

首先生成一個.cpp文件,包含一些平台檢測的#ifdef

/* Identify known platforms by name. */
#if defined(__linux) || defined(__linux__) || defined(linux)
# define PLATFORM_ID "Linux"

#elif defined(__CYGWIN__)
# define PLATFORM_ID "Cygwin"

#elif defined(__MINGW32__)
# define PLATFORM_ID "MinGW"

#elif defined(__APPLE__)
# define PLATFORM_ID "Darwin"

#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
# define PLATFORM_ID "Windows"

#elif defined(__FreeBSD__) || defined(__FreeBSD)
# define PLATFORM_ID "FreeBSD"

...

以及架構檢測的#ifdef

#if defined(_WIN32) defined(_MSC_VER)
# if defined(_M_IA64)
# define ARCHITECTURE_ID "IA64"

# elif defined(_M_X64) || defined(_M_AMD64)
# define ARCHITECTURE_ID "x64"

# elif defined(_M_IX86)
# define ARCHITECTURE_ID "X86"

# elif defined(_M_ARM64)
# define ARCHITECTURE_ID "ARM64"

# elif defined(_M_ARM)
# if _M_ARM == 4
# define ARCHITECTURE_ID "ARMV4I"
# elif _M_ARM == 5
# define ARCHITECTURE_ID "ARMV5I"
# else
# define ARCHITECTURE_ID "ARMV" STRINGIFY(_M_ARM)
# endif

# elif defined(_M_MIPS)
# define ARCHITECTURE_ID "MIPS"

# elif defined(_M_SH)
# define ARCHITECTURE_ID "SHx"

# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif

#elif defined(__WATCOMC__)
# if defined(_M_I86)
# define ARCHITECTURE_ID "I86"

# elif defined(_M_IX86)
# define ARCHITECTURE_ID "X86"

# else /* unknown architecture */
# define ARCHITECTURE_ID ""
# endif

#else
# define ARCHITECTURE_ID
#endif

之後,定義兩個char const*常量。

char const* info_platform = "INFO" ":" "platform[" PLATFORM_ID "]";
char const* info_arch = "INFO" ":" "arch[" ARCHITECTURE_ID "]";

然後把這個cpp編譯成二進位。你以為要運行它?不是的。對於交叉編譯的情況來說,目標平台的二進位是沒法在編譯平台上運行的。所以cmake用了一招更狠的,直接打開二進位文件,搜索字元串。

比如在Windows x64平台上,剛才那兩個常量就會被編譯成INFO:platform[Windows]和INFO:arch[x64]。而這樣的字元串,一定是存在於二進位文件里的。所以只要搜索INFO:platform,後面[]里的就是平台;搜索INFO:arch,後面[]里的就是架構。

同理,cmake里還有對編譯器和編譯器版本等信息的檢測,都是這麼來的。


並沒有什麼真正通用的辦法可以判斷,你只能根據流行的系統的特點一個一個試。

對Windows:

  • 你自己的code是64位的,那你的系統當然是64位的
  • 你的code是32位的情況下,有個東西叫IsWow64Process。這東西如果不存在或者返回false,那就是32位系統,否則就是64位系統。

對Linux/BSD等*NIX系統:

  • 直接開個管道調用uname,他會把內核情況一五一十說清楚。

其他的自己Google吧。

PS:以上還只是針對x86.x64系統。如果考慮到ARMv7和ARMv8,還要自己再查一查。


假如你編譯了一個 32 位 Windows 程序,它在 64 位 Windows 上跑仍然是以 32 位兼容模式運行的,sizeof() 就不對了,它只是檢測編譯平台,而不是運行平台。

如果你用 Qt,這裡有一個很便利的類 QSysInfo,用裡面的 currentCpuArchitecture() 方法:

QSysInfo Class | Qt Core 5.8


操作系統會騙你,所以不存在通用方法


既然你只說跨平台,沒說跨編譯器,而且還必須不用系統API,那麼你能用的就只有編譯器提供的預編譯宏了,你自己去翻翻你用的編譯器的文檔就有了。

至於原理,大致上就是編譯器在安裝/編譯時就幫你判斷好了,然後你在代碼里就可以通過宏開關來判斷了。

如果還想問編譯器是怎麼判斷的?雖然我不十分確定,但估計那多半是調用系統API吧?


請不要把編譯器的鍋甩給 cpp

另外 cpp 跨平台的方法是每個平台都編譯一次,如果不想多次變異的話,請走 Java 的邪道


首先,除了386和amd64之外還有其它架構。

接著假設你題中意思是指的編譯完的程序進行判斷。

最後下結論,既不能判斷CPU,又不能調API,那隻能跟你說,沒有辦法了。sizeof是個常數,編譯期就定了,不可能隨著系統變化而變化。

當然如果是源碼,那編譯器宏或者sizeof(size_t)就完了。


我感覺是無解的,看了一下各種回答也不符合題主描述。

實際工程中跨平台都是用宏定義之類編譯期判斷編譯器和目標平台


你說的是操作系統,64位可以運行32位程序,因此sizeof是沒法判斷的。

跨平台要是cpu也要跨的話,沒辦法判斷。

即便限定x86cpu,也很難判斷。


通過判斷指針的位元組數,sizeof(char*)。


題主你這問題,成心折騰程序員。。

首先得說明一下,不調用系統API,又想知道與系統相關的東西,這就像你讓我進門拿東西,又把門鎖上不給我鑰匙一樣,正統方法是根本不可能做到的(且不深究這麼做有什麼意義,本來一個很簡單的函數就能解決的問題繞個圈)。那就得走曲道破窗而入。

其次是跨平台不是語言本身的功能,是人為去調配的,根據已知的一些信息,去把這個開關拔到win或是linux一端(或其他)。

然後sizeof指令是編譯平台相關的,他不是由當前操作系統的位數決定的,而是由通過編譯器期望生成的可執行文件運行的操作系統決定的,即比如:我編譯器設置好目標的平台是64位win10,那麼sizeof求出來的位元組就和64位win10相匹配。

最後提供曲線進屋方案:

跨平台方法,由宏決定,win下關鍵宏_WIN32等等,linux關鍵宏linux;

操作系統位數判斷方法提供一個不穩定方案:由於64位系統與32位系統在系統文件及系統文件夾上有所不同,由此可以進行對比判斷,如:win平台判斷系統根目錄有無program files(x86)文件夾來判斷,要是不保險可以多找幾個來判斷,同理linux下可以判斷有無/lib64來判斷。。


你只能通過宏知道你的程序是幾位的,高位的操作系統一般都可以有低位的子系統,不用系統 API 的話那隻能做夢了。


要判斷是32還是64位,要分兩個步驟,1)這是哪個操作系統,2)是32位還是64位。

1)這是哪個操作系統。
哪個系統是要靠編譯器去判斷,API、sizeof都是不靠譜的。(https://github.com/freeors/SDL/blob/master/SDL2-2.0.5/include/SDL_platform.h),這個*.h是SDL工程一部分,裡面有各種編譯器判斷。以下我解釋下windows、macosx、iOS和android。

Microsoft Windows
================================
_WIN32:Microsoft 32位/64位 C/C++編譯器內置定義。
_WIN64:Microsoft 64位 C/C++編譯器內置定義。
編譯器內置了兩個宏定義,程序使用時不必包含任何必須的頭文件。
--------------------------------
經常能看到WIN32、_WINDOWS,這些是Visual Studio在創建工程後給的默認定義,不能作為Windows的標準定義。

Apple Mac OS X
================================
__APPLE__:Apple C/C++編譯器內置定義。

Apple iOS
================================
__APPLE__:Apple C/C++編譯器內置定義。
TARGET_OS_IPHONE:TargetConditionals.h內定義。iOS(包括iPad/iPhone/iTouch)真機及模擬器。
TARGET_IPHONE_SIMULATOR:TargetConditionals.h內定義。只針對iOS模擬器。

註:TARGET_OS_IPHONE、TARGET_IPHONE_SIMULATOR是宏,但在Mac OS X這兩個宏也是存在的,只不過值是0!
--------------------------------------------------
Apple C/C++只內置__APPLE__宏定義,但Mac OS X和iOS都定義了這宏,那要如何區分是OS X還是iOS?——使用TARGET_OS_IPHONE宏。程序要滿足「#if defined(__APPLE__) TARGET_OS_IPHONE」時認為是iOS系統。
但TARGET_OS_IPHONE是在TargetConditionals.h內定義的,要使用該宏則必須先#include &,一般代碼得有以下順序。
#if defined(__APPLE__)
#include &
#if TARGET_OS_IPHONE
放置iOS代碼
#else
放置OS X代碼
#endif
#endif

Android
================================
ANDROID:Android GCC編譯器內置定義。
編譯器內置了此個宏定義,程序使用時不必包含任何必須的頭文件。

2)是32位還是64位。
Windows
================================
https://github.com/freeors/SDL/blob/master/SDL2-2.0.5/include/SDL_config_windows.h
看有沒有定義_WIN64

Apple Mac OS X
================================
https://github.com/freeors/SDL/blob/master/SDL2-2.0.5/include/SDL_config_macosx.h
看有沒有定義__LP64__

Apple iOS
================================
https://github.com/freeors/SDL/blob/master/SDL2-2.0.5/include/SDL_config_iphoneos.h
看有沒有定義__LP64__

Android
================================
https://github.com/freeors/SDL/blob/master/SDL2-2.0.5/include/SDL_config_android.h
雖然CPU有32位和64位,但目前代碼上還真沒啥好招


#ifdef __i386

32

#else

64

#endif


就不能先google一下么


判斷宏 _LP64


這個問題同C++沒什麼關係. 你沒有說清楚是運行時還是編譯期判斷, 兩者不同

1. 編譯期

編譯器會告訴目標平台的架構
sizeof是編譯器的常量

2. 運行期

程序當然知道是什麼平台, 因為不同CPU有不同的指令集, 一個二進位程序不能跨平台運行(x86程序不能運行在powerpc上)

同一個平台的64位cpu一般會兼容32位程序, 你可以判斷一個32位的程序是否運行在64位cpu的Compatibility 模式下.

你不想使用系統調用. 就linux來說, 一種方式是利用32位程序在64位機器上的虛擬地址空間同真正32位CPU上的不同. Compatibility 模式下程序可用的地址空間接近4G, 但是32位機器上因為內核空間佔用, 只能用2~3G


寫程式的時候不要使用魔法數字(magical number)就行,用sizeof()代替4,8這樣的


好像有一個TCP/IP服務可以告訴你現在是什麼平台多少位主機。


推薦閱讀:

在鏈表中應該用哪種智能指針比較合理?
什麼時候適合使用 C++ 而不是 C?
除了emoji,有沒有用utf16兩個位元組表示不了而且現代文章/姓名中會使用的cjkv字元么?
c++如何做設計?或者推薦一些比較簡單的開源項目,適合新手練手的。

TAG:編程 | Linux | 計算機科學 | C | CC |