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


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-битные архитектуры.
— Алекс Тингл

В чем разница между 64-битной и 32-битной операциями?
— peterchen

2
Вы действительно не должны связывать это с шириной слова целевой платформы. Вместо этого используйте размер соответствующих типов данных напрямую, чтобы определить, что делать. stdint.hможет быть вашим другом, или вам может потребоваться разработать некоторые собственные typedefs самостоятельно.
— Фил Миллер

Этот тест не работает на Visual Studio 2008 SP1. Он застревает на IS64BIT как для 32-битных, так и для 64-битных.
— Контанго

Ответы:


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). Также вы забыли MIPS64 и IA64 архитектуры в чеке GCC
— Rom

14
@Rom, определенно более двух компиляторов и архитектур. Это всего лишь образец того, как подойти к этой проблеме, а не полное решение.
— JaredPar

2
Я говорю "обычно". «В идеале», вероятно, более реалистично.
— Стив Джессоп

7
Я думаю, что вы должны использовать "#if определенные ( WIN32 ) || определенные (_WIN64)" и т. Д.
— KindDragon

3
#if _WIN32 || _WIN64... #elif __GNUC__... #else # error "Missing feature-test macro for 32/64-bit on this compiler."?
— Дэвислор

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 для этого случая
— Кирилл В. Лядвинский

1
Плавное использование шаблонов и похвал для проверки того, что имеет значение (размер какого-то конкретного типа), а не коррелят.
— Фил Миллер

2
Осторожнее с использованием size_t для этого. Например, могут возникнуть проблемы, когда он не соответствует размеру указателя (например, на платформах с более чем одним размером указателя).
— Логан Капальдо

8
Стандарт говорит, что размер size_tдостаточно велик, чтобы вместить размер любого выделенного объекта в системе. Обычно это то, что вы хотите знать при условной компиляции. Если это не то, что вы хотите, вы можете использовать этот фрагмент с другим типом вместо size_t. Например, это может быть void*.
— Кирилл Васильевич Лядвинский

44

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

  • Оба _WIN32 и _WIN64 иногда и неопределенные, если параметры проекта испорчены или повреждены (особенно на Visual Studio 2008 SP1).
  • Проект с пометкой «Win32» может быть установлен на 64-разрядную версию из-за ошибки конфигурации проекта.
  • В Visual Studio 2008 с пакетом обновления 1 (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) ;. Теперь все это делается во время компиляции :)

Приложение

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

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

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

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


sizeof(void*)это решается во время компиляции или во время выполнения? если это во время компиляции, то во время выполнения проверка всегда будет if(8!=8){...}.
— Амин

@ameen Это решается во время выполнения. Цель этой проверки - убедиться, что программа завершает работу с соответствующей ошибкой, если битность не соответствует ожидаемой. Это означает, что разработчик может немедленно исправить эту ошибку, а не пытаться диагностировать скрытые ошибки, которые появляются позже.
— Контанго

3
4 года спустя (не знаю , если это было возможно раньше) , вы можете преобразовать проверку во время выполнения времени компиляции один с использованием статических проверок: static_assert(sizeof(void*) == 4);. Теперь все это делается во время компиляции :)
— Ал.Г.

1
static_assert(sizeof(void*) * CHAR_BIT == 32)более выразителен и технически корректен (хотя я не знаю ни одной архитектуры, где байты имеют разное количество битов, чем 8)
— Xeverous

1
Смотрите также мой ответ ниже, который сочетает в себе этот отличный ответ с « Лучшими макросами, лучшими флагами » из Fluent C ++.
— металл

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
Почему нет stdint.h для Microsoft? Потому что он был представлен стандартом C99, и Microsoft, похоже, активно отказывается от реализации даже самых простых вещей из C99. Даже простой библиотечный материал, который не требует смены компилятора. Даже то, что уже делается при компиляции для C ++ (например, объявления после операторов). Я знаю, что он нуждается в тестировании и т. Д., Но я также знаю, что MS получает (или когда-то получает) значительную часть своей библиотеки от Dinkumware / Plauger, а Dinkumware уже много лет работает с библиотекой C99.
— Майкл Берр

2
VC ++ 2010 (бета 1, в любом случае) имеет <stdint.h>и <cstdint>. Что касается нынешнего положения вещей - библиотека VC ++ происходит из Dinkumware (все еще делает - TR1 также была взята оттуда), но из того, что я помню, читая в VCBlog, она подвергается довольно значительному рефакторингу для аккуратной компиляции /clr, работы со всеми MSVC нестандартные типы, такие как __int64, и так далее - вот почему это не так просто, как просто взять и поместить в следующую версию компилятора.
— Павел Минаев

2
Это привело меня к правильному ответу, но я думаю, что вы должны сравнить с UINT64_MAX, а не с INT64_MAX. Я использовал SIZE_MAX == UINT64_MAX - вероятно, то же самое
— Арно Duvenhage

15

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


2
К сожалению, sizeof запрещен в директиве #if (если вы думаете об этом, препроцессор не может этого сказать)
— EFraim

Да, именно поэтому я оставил это, предлагая проверять размер указателя, а не использовать sizeof - я не могу придумать портативный способ сделать это на макушке моей головы ...
— mattnewport

3
Вопрос не (пока) говорит , что это имеет быть сделано во время предварительной обработки. Многие / большинство компиляторов, у которых включена оптимизация, выполнят достойную работу по устранению мертвого кода, даже если вы «оставите его до времени выполнения» с помощью теста, подобного sizeof(void*) == 8 ? Do64Bit() : Do32Bit();. Это может все еще оставить неиспользуемую функцию в двоичном файле, но выражение, скорее всего, скомпилировано только для вызова «правильной» функции.
— Стив Джессоп

1
@onebyone, который решает проблему вызовов функций, но что если я захочу объявить переменную другого типа в зависимости от платформы, это нужно будет сделать в препроцессоре, если вы не хотите объявлять несколько переменных и использовать их на основе оператора if ( который также будет оптимизирован, если они не используются, но не будет очень приятным в коде)
— Фалаина

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
Во многих средах программирования для языков C и C на 64-битных компьютерах переменные типа 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 *. Нет гарантии, что int является 64-битным, даже если он скомпилирован для 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 longдолжен быть не менее 64 бит даже в 32-битных системах.


Следует отметить одну вещь: для определения UINT_MAX и ULONG_MAX вы, вероятно, захотите иметь что- #include <limits.h>то перед #ifтестами.
— Алексис Уилк

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>. С помощью Visual C ++, то лучше использовать встроенный компилятор устанавливает: _M_IX86, _M_X64, _M_ARM, _M_ARM64и т.д.
— Чак Walbourn

Для PowerPC, я считаю, нужно проверить __ppc64__, __powerpc64__и _ARCH_PPC64. Это ловит AIX и другие платформы тоже.
— 18-18

1

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

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

РЕДАКТИРОВАТЬ: это общие конфигурации, а не целевые конфигурации. Называй их как хочешь.

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


Или объедините два: автоматически определите конфигурацию компиляторов, о которых вы знаете, но вернитесь к #define, указанному в проекте / командной строке / что-нибудь еще на нераспознанных компиляторах.
— Стив Джессоп

4
Как ваше решение для VisualStudio поможет с кроссплатформенным вопросом OP?
— Алекс Тингл

3
@ Джон: Хм. Они НЕ поддерживаются в любой кроссплатформенной среде по определению . Если это не определение MS кросс-платформенный - работает на более новых версиях Windows.
— EFraim

1
@EFraim: Да, вы можете ЦЕЛИТЬ 32- или 64-битные с использованием VS, но я не об этом говорю. Общие конфигурации проекта и имена, которые я им назначаю, не имеют абсолютно никакого отношения к платформе. Если конфигурации проекта специфичны для VS, то это позор, потому что они очень удобны.
— Джон Зигель

1
Я думаю, что это правильный ответ. Это надежнее, чем пытаться автоматически определять вещи. Все IDE, которые я когда-либо видел, поддерживают эту функцию в той или иной форме, и я уверен, что те, которые я никогда не видел, поддерживают ее тоже. Если вы используете make или jam, вы можете установить переменные из командной строки при вызове обычным способом.

1

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


2
Это было бы похоже на то, чтобы система сборки давала вам такой флаг, как -DBUILD_64BIT. Часто некоторые вещи очень похожи как на 32-, так и на 64-битные, поэтому иметь его в одном файле может быть довольно практичным.
— Алексис Уилке

Поддержка двойных исходных файлов подвержена ошибкам. ИМО даже огромный #if bit64 .. весь код, для 64-битного #else .. весь код, для 32-битного #endif лучше, чем это. (# если строка построчно идеальна, на мой взгляд)
— brewmanz

1

Заимствования из репорт «s отличного ответа выше , и сочетая его с„ Лучше Макросами, Better Flags “из Fluent C ++, вы можете сделать:

// 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]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.