Обнаружение цели в программном обеспечении на С++
Есть ли программный способ определить, находитесь ли вы в архитектуре большого или маленького конца? Мне нужно иметь возможность писать код, который будет выполняться в системе Intel или PPC и использовать точно такой же код (т.е. Нет условной компиляции).
Ответы
Ответ 1
Мне не нравится метод, основанный на типе punning - его часто предупреждает компилятор. Именно для этого нужны профсоюзы!
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
Принцип эквивалентен регистру типов, как это было предложено другими, но это более понятно - и в соответствии с C99 гарантированно будет правильным. GCC предпочитает это по сравнению с прямым указателем.
Это также намного лучше, чем исправление порядка байтов во время компиляции - для ОС, которые поддерживают мульти-архитектуру (например, двоичный файл на Mac OS X), это будет работать как для ppc/i386, так как в противном случае очень легко все испортить,
Ответ 2
Вы можете сделать это, установив int и замаскировав биты, но, вероятно, самый простой способ - просто использовать встроенные операторы преобразования байтов в сети (так как порядок байтов в сети всегда велик).
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
Бит-вождение может быть быстрее, но этот способ прост, прост и довольно невозможен.
Ответ 3
См. в этой статье:
Вот какой код, чтобы определить, что тип вашей машины
int num = 1;
if(*(char *)&num == 1)
{
printf("\nLittle-Endian\n");
}
else
{
printf("Big-Endian\n");
}
Ответ 4
Вы можете использовать std::endian
, если у вас есть доступ к компилятору С++ 20, такому как GCC 8+ или Clang 7+.
Примечание: std::endian
начался в <type_traits>
, но был перемещен в <bit>
на встрече в Кельне в 2019 году. GCC 8, Clang 7, 8 и 9 имеют его в <type_traits>
, в то время как GCC 9+ и Clang 10+ имеют его в <bit>
.
#include <bit>
if constexpr (std::endian::native == std::endian::big)
{
// Big endian system
}
else if constexpr (std::endian::native == std::endian::little)
{
// Little endian system
}
else
{
// Something else
}
Ответ 5
Обычно это делается во время компиляции (особенно по соображениям производительности), используя файлы заголовков, доступные из компилятора или создающие свои собственные. В Linux у вас есть файл заголовка "/usr/include/endian.h"
Ответ 6
Ehm... Меня удивляет, что никто не понял, что компилятор просто оптимизирует тест и поставит фиксированный результат в качестве возвращаемого значения. Это отображает все примеры кода выше, фактически бесполезно. Единственное, что будет возвращено, это континент во время компиляции! И да, я проверил все приведенные выше примеры. Вот пример с MSVC 9.0 (Visual Studio 2008).
Чистый код C
int32 DNA_GetEndianness(void)
{
union
{
uint8 c[4];
uint32 i;
} u;
u.i = 0x01020304;
if (0x04 == u.c[0])
return DNA_ENDIAN_LITTLE;
else if (0x01 == u.c[0])
return DNA_ENDIAN_BIG;
else
return DNA_ENDIAN_UNKNOWN;
}
Демонтажные
PUBLIC _DNA_GetEndianness
; Function compile flags: /Ogtpy
; File c:\development\dna\source\libraries\dna\endian.c
; COMDAT _DNA_GetEndianness
_TEXT SEGMENT
_DNA_GetEndianness PROC ; COMDAT
; 11 : union
; 12 : {
; 13 : uint8 c[4];
; 14 : uint32 i;
; 15 : } u;
; 16 :
; 17 : u.i = 1;
; 18 :
; 19 : if (1 == u.c[0])
; 20 : return DNA_ENDIAN_LITTLE;
mov eax, 1
; 21 : else if (1 == u.c[3])
; 22 : return DNA_ENDIAN_BIG;
; 23 : else
; 24 : return DNA_ENDIAN_UNKNOWN;
; 25 : }
ret
_DNA_GetEndianness ENDP
END
Возможно, для этой функции можно отключить любую оптимизацию времени компиляции, но я не знаю. В противном случае, возможно, его можно жестко скопировать в сборку, хотя это не переносится. И даже тогда даже это может быть оптимизировано. Это заставляет меня думать, что мне нужен действительно дрянной ассемблер, реализовать тот же код для всех существующих процессоров/наборов команд и хорошо... неважно.
Кроме того, кто-то здесь сказал, что во время выполнения endiedness не изменяется. НЕПРАВИЛЬНО. Там есть двухъядерные машины. Их сущность может варьировать выполнение durng. ТАКЖЕ, есть не только Маленький Эндиан и Большой Эндиан, но и другие суждения (какое слово).
Я ненавижу и люблю кодирование одновременно...
Ответ 7
Объявить переменную int:
int variable = 0xFF;
Теперь используйте указатели char * для разных частей и проверьте, что находится в этих частях.
char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;
В зависимости от того, какой из них указывает на 0xFF байт, вы можете определить точность. Для этого требуется sizeof (int) > sizeof (char), но это определенно верно для обсуждаемых платформ.
Ответ 8
Я удивился, что никто не упомянул о макросах, которые по умолчанию определяет препроцессор. Хотя они будут варьироваться в зависимости от вашей платформы; они намного чище, чем писать собственную проверку.
Например; если мы посмотрим на встроенные макросы, которые GCC определяет (на машине X86-64):
:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1
На машине PPC я получаю:
:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1
(Магия :| gcc -dM -E -x c -
печатает все встроенные макросы).
Ответ 9
Для получения дополнительной информации вы можете проверить эту статью кодекса Основные понятия о Endianness:
Как динамически тестировать тип Endian во время выполнения?
Как объясняется в Computer Часто задаваемые вопросы о анимации, вы можете использовать следующую функцию, чтобы узнать, соответствует ли ваш код работает на маленьком или большом конце system: Collapse
#define BIG_ENDIAN 0
#define LITTLE_ENDIAN 1
int TestByteOrder()
{
short int word = 0x0001;
char *byte = (char *) &word;
return(byte[0] ? LITTLE_ENDIAN : BIG_ENDIAN);
}
Этот код присваивает значение 0001h 16-разрядное целое число. Указатель char тогда назначается точка на первом (наименее значимый) байт целочисленное значение. Если первый байт целое число равно 0x01h, тогда система Мало-Endian (0x01h находится в наименьший или наименее значимый, адрес). Если это 0x00h, тогда система - Big-Endian.
Ответ 10
Если вы не используете фреймворк, который был перенесен на процессоры PPC и Intel, вам придется выполнять условные компиляции, поскольку платформы PPC и Intel имеют совершенно разные аппаратные архитектуры, конвейеры, шины и т.д. Это отображает код сборки совершенно разные между ними.
Что касается определения сущности, сделайте следующее:
short temp = 0x1234;
char* tempChar = (char*)&temp;
Вы либо получите tempChar равным 0x12, либо 0x34, из которого вы узнаете утверждение.
Ответ 11
Путь C++ был в том, чтобы использовать boost, где препроцессорные проверки и приведения разделены на части внутри очень тщательно протестированных библиотек.
Библиотека Predef (boost/prefn.h) распознает четыре различных вида порядка байтов.
Библиотека Endian планировалось представить в соответствии со стандартом C++ и поддерживает широкий спектр операций с данными, чувствительными к порядку байтов.
Как указано в ответах выше, Endianness будет частью C++ 20.
Ответ 12
Как указано выше, используйте профсоюзные трюки.
Есть несколько проблем с теми, о которых было сказано выше, хотя наиболее важно, что неприменимый доступ к памяти, как правило, медленный для большинства архитектур, и некоторые компиляторы даже не распознают такие постоянные предикаты вообще, если не выровнять слова.
Поскольку простой endian-тест является скучным, здесь идет (шаблон) функция, которая будет переворачивать ввод/вывод произвольного целого в соответствии с вашим спецификацией, независимо от архитектуры хоста.
#include <stdint.h>
#define BIG_ENDIAN 1
#define LITTLE_ENDIAN 0
template <typename T>
T endian(T w, uint32_t endian)
{
// this gets optimized out into if (endian == host_endian) return w;
union { uint64_t quad; uint32_t islittle; } t;
t.quad = 1;
if (t.islittle ^ endian) return w;
T r = 0;
// decent compilers will unroll this (gcc)
// or even convert straight into single bswap (clang)
for (int i = 0; i < sizeof(r); i++) {
r <<= 8;
r |= w & 0xff;
w >>= 8;
}
return r;
};
Использование:
Чтобы преобразовать заданный endian в host, используйте:
host = endian(source, endian_of_source)
Чтобы преобразовать из эндиана хоста в заданный конец, используйте:
output = endian(hostsource, endian_you_want_to_output)
Результирующий код выполняется так же быстро, как сборка рукописной сборки на clang, на gcc она медленнее (unrolled &, <, → , | для каждого байта), но по-прежнему приличная.
Ответ 13
Я бы сделал что-то вроде этого:
bool isBigEndian() {
static unsigned long x(1);
static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
return result;
}
Вдоль этих строк вы получите эффективную функцию времени, которая будет выполнять только один раз.
Ответ 14
bool isBigEndian()
{
static const uint16_t m_endianCheck(0x00ff);
return ( *((uint8_t*)&m_endianCheck) == 0x0);
}
Ответ 15
union {
int i;
char c[sizeof(int)];
} x;
x.i = 1;
if(x.c[0] == 1)
printf("little-endian\n");
else printf("big-endian\n");
Это еще одно решение. Подобно решению Эндрю Харе.
Ответ 16
Объявляет:
Мой начальный пост неправильно объявлен как "время компиляции". Это не так, даже невозможно в текущем стандарте C++. Constexpr НЕ означает, что функция всегда выполняет вычисления во время компиляции. Спасибо Ричарду Ходжесу за исправление.
время компиляции, немакро, решение C++ 11 constexpr:
union {
uint16_t s;
unsigned char c[2];
} constexpr static d {1};
constexpr bool is_little_endian() {
return d.c[0] == 1;
}
Ответ 17
непроверенный, но, на мой взгляд, это должно работать? потому что это будет 0x01 на маленьком endian, а 0x00 на большой endian?
bool runtimeIsLittleEndian(void)
{
volatile uint16_t i=1;
return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
Ответ 18
Вы также можете сделать это через препроцессор, используя что-то вроде файла заголовка boost, который можно найти boost endian
Ответ 19
Если вам не нужна условная компиляция, вы можете просто написать независимый от пользователя код. Вот пример (взятый из Rob Pike):
Чтение целого числа, сохраненного в little-endian на диске, независимым от конца образом:
i = (data[0]<<0) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24);
Тот же код, пытаясь принять во внимание конечность машины:
i = *((int*)data);
#ifdef BIG_ENDIAN
/* swap the bytes */
i = ((i&0xFF)<<24) | (((i>>8)&0xFF)<<16) | (((i>>16)&0xFF)<<8) | (((i>>24)&0xFF)<<0);
#endif
Ответ 20
int i=1;
char *c=(char*)&i;
bool littleendian=c;
Ответ 21
Как насчет этого?
#include <cstdio>
int main()
{
unsigned int n = 1;
char *p = 0;
p = (char*)&n;
if (*p == 1)
std::printf("Little Endian\n");
else
if (*(p + sizeof(int) - 1) == 1)
std::printf("Big Endian\n");
else
std::printf("What the crap?\n");
return 0;
}
Ответ 22
Если конечный заголовок GCC-only, он предоставляет макросы, которые вы можете использовать.
#include "endian.h"
...
if (__BYTE_ORDER == __LITTLE_ENDIAN) { ... }
else if (__BYTE_ORDER == __BIG_ENDIAN) { ... }
else { throw std::runtime_error("Sorry, this version does not support PDP Endian!");
...
Ответ 23
См. Endianness - иллюстрация кода C-уровня.
// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };
ENDIANESS CheckArchEndianalityV1( void )
{
int Endian = 0x00000001; // assuming target architecture is 32-bit
// as Endian = 0x00000001 so MSB (Most Significant Byte) = 0x00 and LSB (Least Significant Byte) = 0x01
// casting down to a single byte value LSB discarding higher bytes
return (*(char *) &Endian == 0x01) ? LITTLEENDIAN : BIGENDIAN;
}
Ответ 24
Здесь другая версия C. Он определяет макрос, называемый wicked_cast()
для строкового типа, пишущего по объединительным литералам C99 и нестандартному оператору __typeof__
.
#include <limits.h>
#if UCHAR_MAX == UINT_MAX
#error endianness irrelevant as sizeof(int) == 1
#endif
#define wicked_cast(TYPE, VALUE) \
(((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)
_Bool is_little_endian(void)
{
return wicked_cast(unsigned char, 1u);
}
Если целые числа являются однобайтными значениями, endianness не имеет смысла и генерируется ошибка времени компиляции.
Ответ 25
Как компиляторы C (по крайней мере, все, о чем я знаю), работают во время компиляции, когда endive имеет. Даже для биендианных процессоров (например, ARM och MIPS) вы должны выбрать endianness во время компиляции. Кроме того, endianness определяется во всех распространенных форматах файлов для исполняемых файлов (таких как ELF). Хотя можно создать двоичный код биандийского кода (для некоторых эксплойтов ARM-сервера может быть?), Вероятно, это нужно сделать в сборке.
Ответ 26
хотя не существует быстрого и стандартного способа определить его, он выведет его:
#include <stdio.h>
int main()
{
unsigned int i = 1;
char *c = (char*)&i;
if (*c)
printf("Little endian");
else
printf("Big endian");
getchar();
return 0;
}
Ответ 27
Не используйте union
!
C++ не разрешает наказание типов через union
s!
Чтение из поля объединения, которое не было последним записанным полем, является неопределенным поведением !
Многие компиляторы поддерживают это как расширения, но язык не дает никаких гарантий.
Смотрите этот ответ для более подробной информации:
fooobar.com/questions/2966/...
Есть только два правильных ответа, которые гарантированно будут переносимыми.
Первый ответ, если у вас есть доступ к системе, которая поддерживает C++ 20,
использовать std::endian
из заголовка <type_traits>
.
(На момент написания C++ 20 еще не было выпущено, но если что-то не повлияет на включение std::endian
, это будет предпочтительным способом проверки порядка байтов во время компиляции начиная с C++ 20 и далее.)
C++ 20 лет
constexpr bool is_little_endian = (std::endian::native == std::endian::little);
До C++ 20 единственный верный ответ - сохранить целое число, а затем проверить его первый байт через тип punning.
В отличие от использования union
s, это явно разрешено системой типов C++.
Также важно помнить, что для оптимальной мобильности следует использовать static_cast
,
потому что reinterpret_cast
определяется реализацией.
Если программа пытается получить доступ к сохраненному значению объекта через glvalue, отличный от одного из следующих типов, поведение не определено:... тип char
или unsigned char
.
C++ 11 и далее
enum class endianness
{
little = 0,
big = 1,
};
inline endianness get_system_endianness()
{
const int value { 0x01 };
const void * address = static_cast<const void *>(&value);
const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
return (*least_significant_address == 0x01) ? endianness::little : endianness::big;
}
C++ 11 и далее (без перечисления)
inline bool is_system_little_endian()
{
const int value { 0x01 };
const void * address = static_cast<const void *>(&value);
const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
return (*least_significant_address == 0x01);
}
C++ 98/C++ 03
inline bool is_system_little_endian()
{
const int value = 0x01;
const void * address = static_cast<const void *>(&value);
const unsigned char * least_significant_address = static_cast<const unsigned char *>(address);
return (*least_significant_address == 0x01);
}
Ответ 28
Я проходил через учебник: "Компьютерная система: перспектива программиста", и есть проблема, чтобы определить, какой из них представляет собой программа C.
Я использовал функцию указателя, чтобы сделать это следующим образом:
#include <stdio.h>
int main(void){
int i=1;
unsigned char* ii = &i;
printf("This computer is %s endian.\n", ((ii[0]==1) ? "little" : "big"));
return 0;
}
Поскольку int занимает 4 байта, а char занимает всего 1 байт. Мы могли бы использовать указатель char, чтобы указывать на int со значением 1. Таким образом, если компьютер немного конечен, указатель char, на который указывает char, имеет значение 1, в противном случае его значение должно быть 0.
Ответ 29
Как указывал Кориандер, большинство (если не все) этих кодов здесь будут оптимизированы во время компиляции, поэтому сгенерированные двоичные файлы не будут проверять "сущность" во время выполнения.
Было замечено, что данный исполняемый файл не должен запускаться в двух разных байтовых порядках, но я понятия не имею, что это всегда так, и мне кажется, что он взломал меня во время компиляции. Поэтому я закодировал эту функцию:
#include <stdint.h>
int* _BE = 0;
int is_big_endian() {
if (_BE == 0) {
uint16_t* teste = (uint16_t*)malloc(4);
*teste = (*teste & 0x01FE) | 0x0100;
uint8_t teste2 = ((uint8_t*) teste)[0];
free(teste);
_BE = (int*)malloc(sizeof(int));
*_BE = (0x01 == teste2);
}
return *_BE;
}
MinGW не смог оптимизировать этот код, хотя он и оптимизирует другие коды здесь. Я считаю, что это связано с тем, что я оставляю "случайное" значение, которое было распределено по меньшей части памяти байта, как было (по крайней мере, 7 его битов), поэтому компилятор не может знать, что такое случайное значение, и он не оптимизирует функция прочь.
Я также закодировал функцию так, чтобы проверка выполнялась только один раз, и возвращаемое значение сохраняется для следующих тестов.