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 tingle

64 비트와 32 비트 작동의 차이점은 무엇입니까?
peterchen

2
타겟 플랫폼의 워드 너비에서 이것을 조건으로 설정해서는 안됩니다. 대신 관련 데이터 유형의 크기를 직접 사용하여 수행 할 작업을 결정하십시오. stdint.h친구이거나 자신의 적절한 타입 정의를 개발해야 할 수도 있습니다.
Phil Miller

이 테스트는 Visual Studio 2008 SP1에서 작동하지 않는 것 같습니다. 32 비트 및 64 비트 모두 "IS64BIT"에 고정됩니다.
Contango

답변:


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
나는 "보통"이라고 말합니다. "이상적으로"는 아마도 더 현실적 일 것입니다.
Steve Jessop

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

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*.
Kirill V. Lyadvinsky

44

불행히도, 크로스 플랫폼, 크로스 컴파일러 환경에서는 컴파일 타임에이 작업을 수행 할 수있는 신뢰할 수있는 단일 방법이 없습니다.

  • 프로젝트 설정에 결함이 있거나 손상된 경우 (특히 Visual Studio 2008 SP1에서) _WIN32 및 _WIN64 모두 정의되지 않을 수 있습니다 .
  • "Win32"로 표시된 프로젝트는 프로젝트 구성 오류로 인해 64 비트로 설정 될 수 있습니다.
  • Visual Studio 2008 SP1에서 현재 #define에 따라 인텔리전스가 코드의 올바른 부분을 회색으로 표시하지 않는 경우가 있습니다. 이로 인해 컴파일시 어떤 #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) ;. 이제 모든 컴파일 시간에 완료되었습니다 :)

부록

기본적으로 위의 규칙은 전체 코드베이스를보다 안정적으로 만들 수 있습니다.

  • 모든 if () 문은 경고 또는 오류를 생성하는 "else"로 끝납니다.
  • 모든 switch () 문은 경고 또는 오류를 생성하는 "default :"로 끝납니다.

이것이 잘 작동하는 이유는 모든 사례를 미리 생각하고 올바른 코드를 실행하기 위해 "else"부분의 (때로는 결함이있는) 논리에 의존하지 않기 때문입니다.

나는이 기술을 (다른 많은 것들 중에서도) 처음으로 생산 한 날로부터 12 개월 전에 완벽하게 작동 한 30,000 개의 라인 프로젝트를 작성하기 위해 사용했다.


sizeof(void*)컴파일 타임이나 런타임에 해결됩니까? 컴파일 타임에 있으면 런타임에 검사는 항상입니다 if(8!=8){...}.
Ameen

@ameen 런타임에 해결되었습니다. 이 검사의 목적은 비트가 예상과 다른 경우 프로그램이 적절한 오류로 종료되도록하는 것입니다. 이는 개발자가 나중에 나타나는 미묘한 버그를 진단하지 않고이 오류를 즉시 수정할 수 있음을 의미합니다.
Contango

3
4 년 후 (이전에 가능했는지 알지 못함) 정적 assert를 사용하여 런타임 검사를 컴파일 타임으로 변환 할 수 있습니다 static_assert(sizeof(void*) == 4);. 이제 모든 컴파일 시간에 완료되었습니다 :)
Al.G.

1
static_assert(sizeof(void*) * CHAR_BIT == 32)보다 표현적이고 기술적으로 정확합니다 (바이트가 8과 다른 비트 수를 갖는 아키텍처는 알지
못하지만

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에서 라이브러리의 상당 부분을 얻었고 Dinkumware는 몇 년 동안 C99 라이브러리를 가지고 있다는 것을 알고 있습니다.
Michael Burr

2
VC ++ 2010 (베타 1, 어쨌든)에는 <stdint.h>및이 <cstdint>있습니다. 현재 상태-VC ++ 라이브러리는 Dinkumware (여전히 -TR1도 거기에서 가져 왔습니다)에서 비롯되었지만 VCBlog에서 읽은 것을 기억하면서 깨끗하게 컴파일 /clr하고 모든 MSVC와 함께 작업 하기 위해 상당히 중요한 리팩토링을 거칩니다. 와 같은 비표준 유형 등 __int64-그래서 그것을 가져 와서 다음 컴파일러 버전에 넣는 것만 큼 간단하지 않습니다.
Pavel Minaev

2
이것은 정답으로 이어졌지만 INT64_MAX가 아닌 UINT64_MAX와 비교해야한다고 생각합니다. 나는 SIZE_MAX == UINT64_MAX를 사용했습니다-같은 것을 시험하십시오
Arno Duvenhage

15

Windows에서는 처음에는 작동하지 않습니다. Long과 int는 32 비트 또는 64 비트 창을 컴파일 할 때 32 비트입니다. 포인터의 크기가 8 바이트인지 확인하는 것이 더 안정적인 경로 일 것입니다.


2
불행히도 sizeof는 #if 지시어에서 금지되어 있습니다 (전처리 기가 말할 방법이 없다고 생각한다면)
EFraim

그렇기 때문에 sizeof를 사용하는 대신 포인터의 크기를 확인하라는 제안을 남겼습니다.
mattnewport

3
질문은 (아직)가 말을하지 않는 사전 프로세서 시간에서 할 수 있습니다. 최적화를 사용하는 많은 / 대부분의 컴파일러는 "런타임까지 남길"같은 테스트를 수행하더라도 데드 코드를 제거하는 적절한 작업을 수행합니다 sizeof(void*) == 8 ? Do64Bit() : Do32Bit();. 그래도 바이너리에 사용되지 않은 함수가 남을 수 있지만 표현식은 "오른쪽"함수를 호출하기 위해 컴파일 될 가능성이 높습니다.
Steve Jessop

1
함수 호출의 문제를 해결하는 @onebyone이지만 플랫폼에 따라 다른 유형의 변수를 선언하려면 여러 변수를 선언하고 if 문을 기반으로 사용하지 않는 한 전 처리기에서 수행해야합니다 ( 사용되지 않으면 최적화되지만 코드에서는 그리 즐겁지 않습니다)
Falaina

1
그렇다면 당신은 옳습니다. 조건부 상수 표현은 좋지 않습니다. Kirill의 접근 방식은 당신이 원하는 것을 할 수 있습니다.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;
Steve Jessop

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
Hermes

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 비트를 허용 int들과sizeof(int *) != sizeof(char *)

64 비트 플랫폼에 대한 다양한 프로그래밍 모델 이 있습니다.

최선의 방법은 플랫폼 별 테스트입니다. 두 번째로 가장 좋은 휴대용 결정 은 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 long32 비트 시스템에서도 64 비트 이상이어야합니다.


한 가지 주목할 점은 UINT_MAX 및 ULONG_MAX를 정의하려면 테스트 #include <limits.h>전에 어딘가에 있고 싶을 것입니다 #if.
Alexis Wilke

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과 다른 비트 수를 갖는 아키텍처는 알지
못하지만

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, 등
척 Walbourn

파워, 나는 당신을 위해 확인해야 믿고 __ppc64__, __powerpc64__하고 _ARCH_PPC64. 그것은 AIX와 다른 플랫폼에도 적용됩니다.
jww

1

모든 환경에서 프로젝트 구성을 사용할 수 있으면 64 비트 및 32 비트 기호를 쉽게 정의 할 수 있습니다. 따라서 다음과 같은 프로젝트 구성이 있습니다.

32 비트 디버그
32 비트 릴리스
64 비트 디버그
64 비트 릴리스

편집 : 이들은 대상 구성이 아닌 일반 구성입니다. 당신이 원하는대로 전화하십시오.

그렇게 할 수 없다면 나는 Jared의 아이디어를 좋아한다.


또는 두 가지를 결합하십시오 : 알고있는 컴파일러의 구성을 자동 감지하지만 인식 할 수없는 컴파일러의 프로젝트 / 명령 줄 / 지정된 것에 정의 된 #define으로 돌아갑니다.
Steve Jessop

4
Visual Studio 관련 솔루션이 OP의 크로스 플랫폼 질문에 어떻게 도움이됩니까?
alex tingle

3
@Jon : 흠. 정의 에 따라 모든 종류의 크로스 플랫폼 환경에서 지원되지 않습니다 . 크로스 플랫폼에 대한 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

트윈 소스 파일을 유지 관리하면 오류가 발생하기 쉽습니다. IMO조차도 거대한 #if bit64 .. 64bit #else ..에 대한 모든 코드 .. 32bit #endif에 대한 모든 코드가 그보다 낫습니다. (
#if

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.