C ++での32ビットと64ビットの判別


136

C ++コードが32ビット対64ビットでコンパイルされているかどうかを確実に判断する方法を探しています。マクロを使用して合理的な解決策であると考えられるものを思いつきましたが、これが失敗する可能性のあるケースを人々が考えることができるかどうか、またはこれを行うより良い方法があるかどうか知りたいと思いました。これは、クロスプラットフォームの複数コンパイラ環境で実行しようとしていることに注意してください。

#if ((ULONG_MAX) == (UINT_MAX))
# define IS32BIT
#else
# define IS64BIT
#endif

#ifdef IS64BIT
DoMy64BitOperation()
#else
DoMy32BitOperation()
#endif

ありがとう。


8
アーキテクチャのワードサイズが本当に気になる場合は、32ビットでも64ビットでもない可能性を見落とさないでください。世の中には16ビットと128ビットのアーキテクチャがあります。
alex

64ビットと32ビットの動作の違いは何ですか?
peterchen 2009年

2
これをターゲットプラットフォームのワード幅で条件付けしないでください。代わりに、関連するデータ型のサイズを直接使用して、何をするかを決定します。stdint.hあなたの友達かもしれませんし、あなた自身の適切なtypedefを開発する必要があるかもしれません。
Phil Miller、

このテストは、Visual Studio 2008 SP1では機能しないようです。32ビット版と64ビット版の両方で「IS64BIT」が表示されない
Contango 2012

回答:


99

残念ながら、主要なコンパイラ全体で32/64ビットを定義するクロスプラットフォームマクロはありません。これを行う最も効果的な方法は次のとおりです。

まず、自分の表現を選びます。ENVIRONMENT64 / ENVIRONMENT32が好きです。次に、すべての主要なコンパイラーが64ビット環境かどうかを判断するために使用するものを見つけ、それを使用して変数を設定します。

// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENVIRONMENT64
#else
#define ENVIRONMENT32
#endif
#endif

別の簡単な方法は、コンパイラーのコマンドラインからこれらの変数を設定することです。


3
まあ、GCCとVS以外にも他のコンパイラが存在します。たとえば、QNXとGHSが思い浮かびます(ただし、QNXにはGCCと同様のビルドタイム定義があると思います)。また、GCCチェックでMIPS64およびIA64アーキテクチャを忘れました
Rom

14
@Rom、間違いなく2つ以上のコンパイラとアーキテクチャ。これは、この問題への取り組み方のサンプルであり、完全なソリューションではありません。
JaredPar

2
「ふつう」と言います。「理想的に」はおそらくもっと現実的です。
スティーブジェソップ

7
「#if defined(WIN32)|| defined(_WIN64)」などを使用する必要があると思います
KindDragon

3
#if _WIN32 || _WIN64... #elif __GNUC__... #else # error "Missing feature-test macro for 32/64-bit on this compiler."
Davislor 2015年

100
template<int> void DoMyOperationHelper();

template<> void DoMyOperationHelper<4>() 
{
  // do 32-bits operations
}

template<> void DoMyOperationHelper<8>() 
{
  // do 64-bits operations
}

// helper function just to hide clumsy syntax
inline void DoMyOperation() { DoMyOperationHelper<sizeof(size_t)>(); }

int main()
{
  // appropriate function will be selected at compile time 
  DoMyOperation(); 

  return 0;
}

2
size_tが4でも8でもない場合はどうなりますか?
Jesper、

16
@Jesperの場合、上記のサンプルでリンクエラーが発生します。それとも、そのような場合のためにDoMyOperationを実装することができ
キリルV. Lyadvinsky

1
相関関係ではなく、重要なもの(特定のタイプのサイズ)をテストするためのテンプレートと称賛の巧妙な使用。
Phil Miller、

2
これにsize_tを使用する場合は注意してください。たとえば、ポインタサイズに対応しない問題が発生する可能性があります(たとえば、複数のポインタサイズを持つプラットフォーム)。
Logan Capaldo

8
標準では、サイズはsize_tシステムに割り当てられたオブジェクトのサイズを保持するのに十分な大きさであると述べています。通常、これは条件付きコンパイル中に知りたいことです。それが必要なものでない場合は、このスニペットをの代わりに他のタイプで使用できますsize_t。たとえば、それは可能性がありますvoid*
キリルV.リャドビンスキー

44

残念ながら、クロスプラットフォーム、クロスコンパイラ環境では、純粋にコンパイル時にこれを実行する単一の信頼できる方法はありません。

  • プロジェクトの設定に欠陥があるか破損している場合(特にVisual Studio 2008 SP1の場合)、_ WIN32と_WIN64の両方が未定義になることがあります。
  • 「Win32」というラベルの付いたプロジェクトは、プロジェクト構成エラーのため、64ビットに設定されている可能性があります。
  • 現在の#defineによると、Visual Studio 2008 SP1では、インテリセンスがコードの正しい部分をグレー表示しない場合があります。これにより、コンパイル時に使用されている#defineを正確に確認することが難しくなります。

したがって、唯一の信頼できる方法は、3つの単純なチェックを組み合わせることです。

  • 1)コンパイル時間設定、および;
  • 2)ランタイムチェック、および;
  • 3)堅牢なコンパイル時間チェック

簡単なチェック1/3:コンパイル時間設定

必要な#define変数を設定する方法を選択します。@JaredParのメソッドをお勧めします。

// Check windows
#if _WIN32 || _WIN64
   #if _WIN64
     #define ENV64BIT
  #else
    #define ENV32BIT
  #endif
#endif

// Check GCC
#if __GNUC__
  #if __x86_64__ || __ppc64__
    #define ENV64BIT
  #else
    #define ENV32BIT
  #endif
#endif

シンプルチェック2/3:ランタイムチェック

main()で、sizeof()が意味があるかどうかをダブルチェックします。

#if defined(ENV64BIT)
    if (sizeof(void*) != 8)
    {
        wprintf(L"ENV64BIT: Error: pointer should be 8 bytes. Exiting.");
        exit(0);
    }
    wprintf(L"Diagnostics: we are running in 64-bit mode.\n");
#elif defined (ENV32BIT)
    if (sizeof(void*) != 4)
    {
        wprintf(L"ENV32BIT: Error: pointer should be 4 bytes. Exiting.");
        exit(0);
    }
    wprintf(L"Diagnostics: we are running in 32-bit mode.\n");
#else
    #error "Must define either ENV32BIT or ENV64BIT".
#endif

シンプルなチェック3/3:堅牢なコンパイル時間チェック

一般的なルールは、「すべての#defineはエラーを生成する#elseで終わる必要がある」です。

#if defined(ENV64BIT)
    // 64-bit code here.
#elif defined (ENV32BIT)
    // 32-bit code here.
#else
    // INCREASE ROBUSTNESS. ALWAYS THROW AN ERROR ON THE ELSE.
    // - What if I made a typo and checked for ENV6BIT instead of ENV64BIT?
    // - What if both ENV64BIT and ENV32BIT are not defined?
    // - What if project is corrupted, and _WIN64 and _WIN32 are not defined?
    // - What if I didn't include the required header file?
    // - What if I checked for _WIN32 first instead of second?
    //   (in Windows, both are defined in 64-bit, so this will break codebase)
    // - What if the code has just been ported to a different OS?
    // - What if there is an unknown unknown, not mentioned in this list so far?
    // I'm only human, and the mistakes above would break the *entire* codebase.
    #error "Must define either ENV32BIT or ENV64BIT"
#endif

2017-01-17の更新

からのコメント@AI.G

4年後(以前は可能だったかどうかわからない)に、静的アサートを使用してランタイムチェックをコンパイル時のチェックに変換できます:static_assert(sizeof(void *)== 4);。今ではすべてコンパイル時に行われます:)

付録A

偶然にも、上記のルールはコードベース全体の信頼性を高めるために適応させることができます:

  • すべてのif()ステートメントは、警告またはエラーを生成する「else」で終わります。
  • すべてのswitch()ステートメントは、警告またはエラーを生成する「デフォルト:」で終わります。

これがうまく機能する理由は、正しいコードを実行するために、「else」部分の(時々欠陥のある)ロジックに依存せずに、すべてのケースを事前に考えることを強制するためです。

私はこのテクニックを(他の多くの中で)30,000ラインプロジェクトを作成するために使用しました。このプロジェクトは、最初に運用環境に配置された日(12か月前)から完璧に機能しました。


sizeof(void*)コンパイル時または実行時に解決されますか?コンパイル時の場合、実行時のチェックは常にになりますif(8!=8){...}
Ameen、2015年

@ameen実行時に解決されます。このチェックの目的は、ビット数が予期したものでない場合に、プログラムが適切なエラーで終了することを確認することです。これは、開発者が後でポップアップする微妙なバグを診断するのではなく、このエラーをすぐに修正できることを意味します。
Contango、2015年

3
4年後(以前は可能だったかどうかわからない)に、静的アサートを使用してランタイムチェックをコンパイル時のチェックに変換できますstatic_assert(sizeof(void*) == 4);。今ではすべてコンパイル時に完了します:)
Al.G.

1
static_assert(sizeof(void*) * CHAR_BIT == 32)より表現力があり、技術的に正しい(ただし、バイトのビット数が8と異なるアーキテクチャは知らない)
Xeverous

1
この優れた回答とFluent C ++の「Better Macros、Better Flags」を組み合わせた以下の私の回答も参照してください。
金属

30

で定義されているマクロを使用できるはずstdint.hです。特にINTPTR_MAX、まさにあなたが必要とする価値です。

#include <cstdint>
#if INTPTR_MAX == INT32_MAX
    #define THIS_IS_32_BIT_ENVIRONMENT
#elif INTPTR_MAX == INT64_MAX
    #define THIS_IS_64_BIT_ENVIRONMENT
#else
    #error "Environment not 32 or 64-bit."
#endif

Microsoftのコンパイラの一部(すべて?)バージョンには、が付属していませんstdint.h。標準ファイルなので、理由はわかりません。使用できるバージョンは次のとおりです。http://msinttypes.googlecode.com/svn/trunk/stdint.h


4
Microsoftにstdint.hがないのはなぜですか?それはC99標準で導入されたため、MicrosoftはC99からの最も簡単なものでさえ実装することに積極的な嫌悪感を持っているようです。コンパイラーの変更を必要としない簡単なライブラリーもです。C ++のコンパイル時に既に行われていること(ステートメントの後の宣言など)も。私はそれがテストなどを必要としていることを知っていますが、MSがそのライブラリのかなりの部分をDinkumware / Plaugerから取得した(または一度取得した)ことも知っています。
マイケルバー

2
VC ++ 2010(とにかくベータ1)にはとが<stdint.h>あり<cstdint>ます。現状について-VC ++ライブラリはDinkumwareに由来します(まだそうです-TR1もそこから取得されました)が、VCBlogで読んだことを思い出すと、それはで完全にコンパイルされ/clr、すべてのMSVCで動作するようにかなり重要なリファクタリングを受けますなどの非標準の型など__int64-これは、それを取得して次のコンパイラバージョンに組み込むほど単純ではない理由です。
Pavel Minaev、2009年

2
これは私に正しい答えを導きましたが、INT64_MAXではなくUINT64_MAXと比較する必要があると思います。私はSIZE_MAX == UINT64_MAXを使用しました-同じ可能性があります
Arno Duvenhage

15

そもそもWindowsでは機能しません。longとintは、32ビットと64ビットのどちらのウィンドウ用にコンパイルするかに関係なく、どちらも32ビットです。ポインタのサイズが8バイトかどうかを確認する方が、おそらくより信頼できるルートだと思います。


2
残念ながら、sizeifは#ifディレクティブで禁止されています(それについて考えた場合、プリプロセッサーに伝える方法がない)
EFraim

はい、そういうわけで、私はsizeofを使用するのではなく、ポインターのサイズをチェックすることを提案することにしました
mattnewport

3
質問では、(まだ)プリプロセッサ時に実行する必要があると言われいません。のようなテストで「実行時まで残す」場合でも、最適化がオンになっている多くの/ほとんどのコンパイラは、デッドコードを排除する適切な仕事をしますsizeof(void*) == 8 ? Do64Bit() : Do32Bit();。それでも未使用の関数がバイナリに残る可能性がありますが、式は「正しい」関数の呼び出しだけにコンパイルされる可能性があります。
スティーブジェソップ

1
関数呼び出しの問題を解決する@onebyoneですが、プラットフォームに基づいて異なるタイプの変数を宣言したい場合、複数の変数を宣言してifステートメントに基づいてそれらを使用する場合を除き、プリプロセッサーで行う必要があります(未使用の場合も最適化されますが、コードではあまり快適ではありません)
Falaina

1
それからあなたは正しい、条件付きの定数式はダメです。しかし、キリルのアプローチはあなたが望むことをすることができます:template<int> struct Thing; template<> struct Thing<4> { typedef uint32_t type; }; template<> struct Thing<8> { typedef uint64_t type; }; typedef Thing<sizeof(void*)>::type thingtype;
スティーブ・ジェソップ

9

あなたはこれを行うことができます:

#if __WORDSIZE == 64
char *size = "64bits";
#else
char *size = "32bits";
#endif

1
64ビットマシン上のCおよびC派生言語の多くのプログラミング環境では、「int」変数は依然として32ビット幅ですが、長整数およびポインタは64ビット幅です。これらは、LP64データモデルを持っていると説明されています。unix.org/version2/whatsnew/lp64_wp.html
エルメス

6
Try this:
#ifdef _WIN64
// 64 bit code
#elif _WIN32
// 32 bit code
#else
   if(sizeof(void*)==4)

       // 32 bit code
   else 

       // 64 bit code   
#endif

7
このコードは正しくありません。64ビットでは、_WIN32と_WIN64の両方が定義されています。逆にすれば(_WIN64の最初のチェック)、もちろん動作します。
BertR

4

「64ビットでコンパイル」はC ++では十分に定義されていません。

C ++は、int、long、などのサイズの下限のみを設定しますvoid *。64ビットプラットフォーム用にコンパイルされた場合でも、intが64ビットである保証はありません。モデルでは、たとえば23ビットintsとsizeof(int *) != sizeof(char *)

64ビットプラットフォームにはさまざまなプログラミングモデルがあります。

あなたの最善の策は、プラットフォーム固有のテストです。2番目に優れた移植可能な決定は、64ビットとは何かにより具体的でなければなりません。


3

あなたのアプローチはそれほど遠くはありませんでしたが、同じサイズであるかどうかlongを確認しているだけintです。理論的には、どちらも64ビットである可能性があります。その場合、両方が32ビットであると想定すると、チェックは失敗します。以下は、相対的なサイズではなく、タイプ自体のサイズを実際にチェックするチェックです。

#if ((UINT_MAX) == 0xffffffffu)
    #define INT_IS32BIT
#else
    #define INT_IS64BIT
#endif
#if ((ULONG_MAX) == 0xfffffffful)
    #define LONG_IS32BIT
#else
    #define LONG_IS64BIT
#endif

原則として、最大値を持つシステム定義のマクロがあるすべての型に対してこれを行うことができます。

標準はlong long、32ビットシステムでも少なくとも64ビットである必要があることに注意してください。


注意すべきことの1つは、UINT_MAXとULONG_MAXを定義するために、おそらくテストの#include <limits.h>前のどこかに置くことです#if
Alexis Wilke、2015

3

人々はすでに、プログラムが32-bitまたはでコンパイルされているかどうかを判断しようとする方法を提案しました64-bit

また、c ++ 11機能static_assertを使用して、アーキテクチャが意図したとおりであることを確認できることを追加したいと思います(「リラックスする」)。

したがって、マクロを定義する場所で:

#if ...
# define IS32BIT
  static_assert(sizeof(void *) == 4, "Error: The Arch is not what I think it is")
#elif ...
# define IS64BIT
  static_assert(sizeof(void *) == 8, "Error: The Arch is not what I think it is")
#else
# error "Cannot determine the Arch"
#endif

static_assert(sizeof(void*) * CHAR_BIT == 32)より表現力があり、技術的に正しい(ただし、バイトのビット数が8と異なるアーキテクチャは知らない)
Xeverous

2

以下のコードは、現在のほとんどの環境で問題なく機能します。

  #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) &&     !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
    #define IS64BIT 1
 #else
    #define IS32BIT 1
#endif

3
_WIN64既に含まれている必要があることに注意してください<windows.h>。:ビジュアルC ++で、それは内蔵のコンパイラ定義使用することをお勧めします_M_IX86_M_X64_M_ARM_M_ARM64、など
チャック・ウォルボーン

PowerPCのために、私はあなたがチェックする必要があると考えている__ppc64____powerpc64___ARCH_PPC64。これは、AIXやその他のプラットフォームにも当てはまります。
jww 2018年

1

すべての環境でプロジェクト構成を使用できれば、64ビットと32ビットのシンボルを簡単に定義できます。だからあなたはこのようなプロジェクト構成を持っているでしょう:

32ビットデバッグ
32ビットリリース
64ビットデバッグ
64ビットリリース

編集:これらはターゲット設定ではなく、一般的な設定です。好きなように呼んでください。

それができない場合は、Jaredのアイデアが気に入っています。


または、2つを組み合わせます。知っているコンパイラの設定を自動検出しますが、認識されていないコンパイラのプロジェクト/コマンドライン/何でも指定された#defineに戻ります。
スティーブジェソップ

4
VisualStudio固有のソリューションは、OPのクロスプラットフォームの問題にどのように役立ちますか?
alex tingle

3
@ジョン:うーん。これらは、定義上、どのようなクロスプラットフォーム環境でもサポートされていません。MSのクロスプラットフォームの定義でない限り、Windowsの新しいフレーバーで動作します。
EFraim

1
@EFraim:はい、VSを使用して32ビットまたは64ビットをターゲットにすることができますが、それは私が話していることではありません。一般的なプロジェクト構成、および私がそれらに割り当てる名前は、プラットフォームとはまったく関係ありません。プロジェクト構成がVS固有である場合、非常に便利であるため、それは残念です。
Jon Seigel、

1
これが正解だと思います。自動検出を試みるよりも信頼性が高くなります。今まで見たすべてのIDEがこの機能を何らかの形でサポートしており、今まで見たことのないIDEもサポートしているに違いありません。makeまたはjamを使用する場合は、通常の方法で、起動時にコマンドラインから変数を設定できます。

1

32ビットと64ビットのソースを異なるファイルに配置し、ビルドシステムを使用して適切なソースファイルを選択します。


2
これは、ビルドシステムにのようなフラグを付けるのと似ています-DBUILD_64BIT。多くの場合、特定の事柄は32ビットと64ビットの両方に非常に似ているため、同じファイルに含めると非常に実用的です。
Alexis Wilke 2014年

ツインソースファイルを維持すると、エラーが発生しやすくなります。IMOでさえ#if bit64 ..すべてのコード、64ビットの#else ..すべてのコード、32ビットの#endifの方がそれより優れています。(
#ifの1行ずつ

1

上記Contango優れた答えを借り、それをFluent C ++の「Better Macros、Better Flags」と組み合わせると、次のことができます。

// Macro for checking bitness (safer macros borrowed from 
// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/)
#define MYPROJ_IS_BITNESS( X ) MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_##X()

// Bitness checks borrowed from https://stackoverflow.com/a/12338526/201787
#if _WIN64 || ( __GNUC__ && __x86_64__ )
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 1
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 0
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x64)
    static_assert( sizeof( void* ) == 8, "Pointer size is unexpected for this bitness" );
#elif _WIN32 || __GNUC__
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_64() 0
#    define MYPROJ_IS_BITNESS_PRIVATE_DEFINITION_32() 1
#    define MYPROJ_IF_64_BIT_ELSE( x64, x86 ) (x86)
    static_assert( sizeof( void* ) == 4, "Pointer size is unexpected for this bitness" );
#else
#    error "Unknown bitness!"
#endif

その後、次のように使用できます。

#if MYPROJ_IS_BITNESS( 64 )
    DoMy64BitOperation()
#else
    DoMy32BitOperation()
#endif

または、追加した追加のマクロを使用します。

MYPROJ_IF_64_BIT_ELSE( DoMy64BitOperation(), DoMy32BitOperation() );

0

この回答をユースケースとして追加し、別の回答で説明されているランタイムチェックの完全な例を示します。

これは、プログラムが64ビットと32ビットのどちらでコンパイルされたのか(あるいは、その他について)エンドユーザーに伝えるために私が取っているアプローチです。

version.h

#ifndef MY_VERSION
#define MY_VERSION

#include <string>

const std::string version = "0.09";
const std::string arch = (std::to_string(sizeof(void*) * 8) + "-bit");

#endif

test.cc

#include <iostream>
#include "version.h"

int main()
{
    std::cerr << "My App v" << version << " [" << arch << "]" << std::endl;
}

コンパイルとテスト

g++ -g test.cc
./a.out
My App v0.09 [64-bit]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.