Определение 32 против 64 бит в С++

Я ищу способ достоверно определить, скомпилирован ли код С++ в 32 и 64 бит. Мы придумали разумное решение с использованием макросов, но было любопытно узнать, могут ли люди думать о случаях, когда это может закончиться неудачей или если есть лучший способ сделать это. Обратите внимание, что мы пытаемся сделать это в кросс-платформенной среде с несколькими компиляторами.

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

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

Спасибо.

Ответ 1

К сожалению, нет кросс-платформенного макроса, который определяет 32/64 бит в основных компиляторах. Я нашел наиболее эффективный способ сделать это следующее.

Сначала я выбираю свое собственное представление. Я предпочитаю ОКРУЖЕНИЕ64/ОКРУЖАЮЩУЮ СРЕДУ32. Затем я узнаю, что используют все основные компиляторы для определения, является ли это 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

Еще один простой способ - просто установить эти переменные из командной строки компилятора.

Ответ 2

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;
}

Ответ 3

К сожалению, в кросс-платформенной среде кросс-компилятора нет единого надежного метода для этого во время компиляции.

  • Оба _WIN32 и _WIN64 могут иногда и быть undefined, если настройки проекта ошибочны или повреждены (особенно в Visual Studio 2008 SP1).
  • Проект с меткой "Win32" может быть установлен на 64-разрядный, из-за ошибки конфигурации проекта.
  • В Visual Studio 2008 SP1 иногда intellisense не выделяет правильные части кода в соответствии с текущим #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: static_assert (sizeof (void *) == 4);. Теперь все это сделано во время компиляции:)

Приложение A

Кстати, приведенные выше правила могут быть адаптированы, чтобы сделать вашу всю кодовую базу более надежной:

  • Каждый оператор if() заканчивается в "else", который генерирует предупреждение или ошибку.
  • Каждый оператор switch() заканчивается "по умолчанию:", который генерирует предупреждение или ошибку.

Причина, по которой это хорошо работает, заключается в том, что она заставляет вас думать о каждом отдельном случае заранее и не полагаться на (иногда ошибочную) логику в части "else" для выполнения правильного кода.

Я использовал эту технику (среди многих других) для написания проекта на 30 000 строк, который работал безупречно со дня его первого развертывания в производство (это было 12 месяцев назад).

Ответ 4

Вы должны иметь возможность использовать макросы, определенные в 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 p >

Ответ 5

Это не будет работать в Windows для начала. Longs и ints - 32 бита, независимо от того, компилируете ли вы для 32-битных или 64-битных окон. Я бы подумал, что проверка того, является ли размер указателя 8 байтами, вероятно, более надежным маршрутом.

Ответ 6

Вы можете сделать это:

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

Ответ 7

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

Ответ 8

"Скомпилировано в 64-битном формате" не определено в С++.

С++ устанавливает только нижние пределы для таких размеров, как int, long и void *. Нет никакой гарантии, что int 64 бит, даже если он скомпилирован для 64-битной платформы. Модель позволяет, например, 23 бит int и sizeof(int *) != sizeof(char *)

Существуют различные модели программирования для 64-разрядных платформ.

Лучше всего провести тест на платформу. Ваше второе лучшее, переносное решение должно быть более конкретным в 64-битной версии.

Ответ 9

Люди уже предложили методы, которые будут пытаться определить, скомпилирована ли программа в 32-bit или 64-bit.

И я хочу добавить, что вы можете использовать функцию С++ 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

Ответ 10

Ваш подход не был слишком далек, но вы проверяете только то, что 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 быть не менее 64 бит даже в 32-битных системах.

Ответ 11

Ниже код работает отлично для большинства современных сред:

  #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

Ответ 12

Если вы можете использовать конфигурацию проекта во всех своих средах, это упростит определение 64- и 32-битного символа. Таким образом, у вас будут такие конфигурации проекта, как это:

32-разрядная отладка
32-разрядная версия
64-разрядная отладка
64-разрядная версия

EDIT: это общие конфигурации, а не целевые конфигурации. Назовите их, как хотите.

Если вы не можете этого сделать, мне нравится идея Джареда.

Ответ 13

Я бы разместил 32-битные и 64-разрядные источники в разных файлах, а затем выделил соответствующие исходные файлы, используя систему сборки.

Ответ 14

Я добавляю этот ответ как пример использования и завершаю пример проверки времени выполнения, описанной в другом ответе.

Это тот подход, который я использовал для передачи конечному пользователю, была ли программа скомпилирована как 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]