C Макроопределение для определения машины с прямым или прямым порядком байтов?
Существует ли однострочное макроопределение для определения порядкового номера машины. Я использую следующий код, но преобразование его в макрос будет слишком длинным.
unsigned char test_endian( void )
{
int test_var = 1;
unsigned char test_endian* = (unsigned char*)&test_var;
return (test_endian[0] == NULL);
}
25 ответов
Код, поддерживающий произвольные порядки байтов, готовый для помещения в файл с именем order32.h
:
#ifndef ORDER32_H
#define ORDER32_H
#include <limits.h>
#include <stdint.h>
#if CHAR_BIT != 8
#error "unsupported char size"
#endif
enum
{
O32_LITTLE_ENDIAN = 0x03020100ul,
O32_BIG_ENDIAN = 0x00010203ul,
O32_PDP_ENDIAN = 0x01000302ul, /* DEC PDP-11 (aka ENDIAN_LITTLE_WORD) */
O32_HONEYWELL_ENDIAN = 0x02030001ul /* Honeywell 316 (aka ENDIAN_BIG_WORD) */
};
static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order =
{ { 0, 1, 2, 3 } };
#define O32_HOST_ORDER (o32_host_order.value)
#endif
Вы бы проверить для систем с прямым порядком байтов через
O32_HOST_ORDER == O32_LITTLE_ENDIAN
Если у вас есть компилятор, который поддерживает составные литералы C99:
#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
или же:
#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
Тем не менее, в общем, вы должны попытаться написать код, который не зависит от порядкового номера хост-платформы.
Пример независимой от хоста реализации порядка байтов ntohl()
:
uint32_t ntohl(uint32_t n)
{
unsigned char *np = (unsigned char *)&n;
return ((uint32_t)np[0] << 24) |
((uint32_t)np[1] << 16) |
((uint32_t)np[2] << 8) |
(uint32_t)np[3];
}
Там нет стандарта, но на многих системах, включая <endian.h>
даст вам некоторые определения для поиска.
Чтобы определить порядок байтов во время выполнения, вы должны иметь возможность обращаться к памяти. Если вы придерживаетесь стандартного C, для объявления переменной в памяти требуется оператор, но для возврата значения требуется выражение. Я не знаю, как сделать это в одном макросе - вот почему у gcc есть расширения:-)
Если вы хотите иметь.h файл, вы можете определить
static uint32_t endianness = 0xdeadbeef;
enum endianness { BIG, LITTLE };
#define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \
: *(const char *)&endianness == 0xde ? BIG \
: assert(0))
и тогда вы можете использовать ENDIANNESS
макрос, как вы будете.
Если вы хотите полагаться только на препроцессор, вам нужно выяснить список предопределенных символов. Препроцессорная арифметика не имеет понятия адресации.
GCC на Mac определяет __LITTLE_ENDIAN__
или же __BIG_ENDIAN__
$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1
Затем вы можете добавить дополнительные условные директивы препроцессора, основанные на обнаружении платформы, такие как #ifdef _WIN32
и т.п.
Я считаю, что это то, о чем просили. Я проверял это только на машине с прямым порядком байтов под msvc. Кто-то, пожалуйста, подтвердите на машине с прямым порядком байтов.
#define LITTLE_ENDIAN 0x41424344UL
#define BIG_ENDIAN 0x44434241UL
#define PDP_ENDIAN 0x42414443UL
#define ENDIAN_ORDER ('ABCD')
#if ENDIAN_ORDER==LITTLE_ENDIAN
#error "machine is little endian"
#elif ENDIAN_ORDER==BIG_ENDIAN
#error "machine is big endian"
#elif ENDIAN_ORDER==PDP_ENDIAN
#error "jeez, machine is PDP!"
#else
#error "What kind of hardware is this?!"
#endif
Как примечание (специфично для компилятора), с агрессивным компилятором вы можете использовать оптимизацию "удаления мертвого кода" для достижения того же эффекта, что и во время компиляции. #if
вот так:
unsigned yourOwnEndianSpecific_htonl(unsigned n)
{
static unsigned long signature= 0x01020304UL;
if (1 == (unsigned char&)signature) // big endian
return n;
if (2 == (unsigned char&)signature) // the PDP style
{
n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
return n;
}
if (4 == (unsigned char&)signature) // little endian
{
n = (n << 16) | (n >> 16);
n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL);
return n;
}
// only weird machines get here
return n; // ?
}
Вышесказанное опирается на тот факт, что компилятор распознает постоянные значения во время компиляции, полностью удаляет код внутри if (false) { ... }
и заменяет код как if (true) { foo(); }
с foo();
В худшем случае: компилятор не выполняет оптимизацию, вы по-прежнему получаете правильный код, но немного медленнее.
Если вы ищете тест на время компиляции и используете gcc, вы можете сделать:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
Смотрите документацию gcc для получения дополнительной информации.
Фактически вы можете получить доступ к памяти временного объекта, используя составной литерал (C99):
#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
Какой GCC будет оценивать во время компиляции.
Если вы сбросите препроцессор #defines
gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null
Обычно вы можете найти то, что вам поможет. С логикой времени компиляции.
#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
Однако разные компиляторы могут иметь разные определения.
'Сетевая библиотека C' предлагает функции для обработки порядка байтов. А именно htons(), htonl(), ntohs() и ntohl ()... где n - это "сеть" (т. Е. Big-endian), а h - это "хост" (т. Е. Порядковый номер машины, на которой выполняется код).
Эти очевидные "функции" (обычно) определяются как макросы [см.
Следующие макросы используют эти "функции" для оценки порядка байтов.
#include <arpa/inet.h>
#define IS_BIG_ENDIAN (1 == htons(1))
#define IS_LITTLE_ENDIAN (!IS_BIG_ENDIAN)
К тому же:
Единственный раз, когда мне нужно знать порядковый номер системы, это когда я записываю переменную [в файл / другое], которая может быть прочитана другой системой с неизвестным порядком байтов (для кроссплатформенной совместимости).)... В таких случаях вы можете предпочесть непосредственное использование функций с порядком байтов:
#include <arpa/inet.h>
#define JPEG_MAGIC (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F')
// Result will be in 'host' byte-order
unsigned long jpeg_magic = JPEG_MAGIC;
// Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable)
unsigned long jpeg_magic = htonl(JPEG_MAGIC);
Используйте встроенную функцию, а не макрос. Кроме того, вам нужно хранить что-то в памяти, что является не очень приятным побочным эффектом макроса.
Вы можете преобразовать его в короткий макрос, используя статическую или глобальную переменную, например так:
static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
Не забывайте, что порядковый номер не вся история - размер char
может быть не 8 бит (например, DSP), отрицание дополнения до двух не гарантируется (например, Cray), может потребоваться строгое выравнивание (например, SPARC, а также ARM-пружины в среднем порядке при отсутствии выравнивания) и т. д. и т. д. и т. д.
Лучше было бы нацеливаться на конкретную архитектуру процессора.
Например:
#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64)
#define USE_LITTLE_ENDIAN_IMPL
#endif
void my_func()
{
#ifdef USE_LITTLE_ENDIAN_IMPL
// Intel x86-optimized, LE implementation
#else
// slow but safe implementation
#endif
}
Обратите внимание, что это решение, к сожалению, также не является ультрапортативным, так как оно зависит от определений, специфичных для компилятора (стандарта нет, но вот хорошая компиляция таких определений).
В то время как нет переносимого #define или чего-то, на что можно было бы положиться, платформы предоставляют стандартные функции для преобразования в и из вашего "хоста" endian.
Как правило, вы делаете хранилище - на диск или в сеть - используя 'endian сети', который является BIG endian, и локальные вычисления, используя endian хоста (который на x86 является LITTLE endian). Ты используешь htons()
а также ntohs()
и друзья, чтобы преобразовать между двумя.
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
Этот вопрос актуален и для cpp, поэтому и спросил здесь.
ТОЛЬКО
#if __cplusplus > 201703L
#include <bit>
#include <iostream>
using namespace std;
int main()
{
if constexpr (endian::native == endian::big)
cout << "big-endian";
else if constexpr (endian::native == endian::little)
cout << "little-endian";
else
cout << "mixed-endian";
}
Для получения дополнительной информации: https://en.cppreference.com/w/cpp/types/endian
Попробуй это:
#include<stdio.h>
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{
TEST;
}
Пожалуйста, обратите внимание, что большинство ответов здесь не являются переносимыми, поскольку современные компиляторы будут оценивать эти ответы во время компиляции (зависит от оптимизации) и возвращать конкретное значение на основе определенного порядкового номера, тогда как фактический порядковый номер машины может отличаться. Значения, на которых проверяется порядок байтов, никогда не достигнут системной памяти, поэтому реальный исполняемый код будет возвращать один и тот же результат независимо от фактического порядка байтов.
Например, в ARM Cortex-M3 реализованная последовательность будет отражаться в бите состояния AIRCR.ENDIANNESS, и компилятор не может знать это значение во время компиляции.
Вывод компиляции для некоторых ответов, предложенных здесь:
https://godbolt.org/z/GJGNE2 для этого ответа,
https://godbolt.org/z/Yv-pyJ для этого ответа и так далее.
Чтобы решить это, вам нужно будет использовать volatile
Классификатор. Yogeesh H T
ответ является наиболее близким для сегодняшнего использования в реальной жизни, но так как Christoph
предлагает более полное решение, небольшое исправление его ответа сделает ответ завершенным, просто добавьте volatile
к декларации профсоюза: static const volatile union
,
Это обеспечит сохранение и чтение из памяти, что необходимо для определения порядка байтов.
В C++20 перечислениеstd::endian
в заголовке<bit>
был представлен:
https://en.cppreference.com/w/cpp/types/endian
#include <bit>
#include <iostream>
int main() {
if constexpr (std::endian::native == std::endian::big)
std::cout << "big-endian\n";
else if constexpr (std::endian::native == std::endian::little)
std::cout << "little-endian\n";
else std::cout << "mixed-endian\n";
}
Если ваш компилятор поддерживает составные литералы и вы явно не используете C++, вы можете использовать
#define BIG_ENDIAN ((*(const char*)&(const int){0x01020304}) == 0x01)
#define LITTLE_ENDIAN ((*(const char*)&(const int){0x01020304}) == 0x04)
Это не требует объявления каких-либо переменных времени выполнения, что, я думаю, делает его намного чище, чем большинство других решений.
#include <stdio.h>
#define CHECK_FOR_ENDIANNESS (*(char*)&(unsigned int){1})
int main() {
if (CHECK_FOR_ENDIANNESS) {
printf("little-endian.\n");
} else {
printf("big-endian.\n");
}
return 0;
}
Как это работает
Step #1 Create an unsigned int with a value of 1
+----------+----------+----------+----------+
Memory Address | 0x1000 | 0x1001 | 0x1002 | 0x1003 |
+----------+----------+----------+----------+
Little: | 01 | 00 | 00 | 00 |
+----------+----------+----------+----------+
Big: | 00 | 00 | 00 | 01 |
+----------+----------+----------+----------+
Step #2 Cast the address to a char pointer (focuses on first byte)
+----------+
Memory Address | 0x1000 |
+----------+
Little: | 01 |
+----------+
Big: | 00 |
+----------+
Step #3 Dereference the char pointer to get the value
Little: Returns 1 (for little-endian)
Big: Returns 0 (for big-endian)
Если ускорение доступно, вы можете использоватьBoost.Predef
который содержит различные предопределенные макросы для целевой платформы, включая endianness (). Да , boost часто считают библиотекой C++, но это заголовок препроцессора, который работает и с C!Это позволяет вам определять порядок байтов во время компиляции переносимо.
Эта библиотека определяет набор компилятора, архитектуры, операционной системы, библиотеки и других номеров версий на основе информации, которую она может собрать из предопределенных макросов C, C++, Objective C и Objective C++ или макросов, определенных в общедоступных заголовках. Идея этой библиотеки возникла из предложения расширить библиотеку Boost Config, чтобы предоставить больше и непротиворечивую информацию, чем определения функций, которые она поддерживает. Далее следует отредактированная версия этого краткого предложения.
Например
#include <boost/predef.h>
#if defined(BOOST_ENDIAN_BIG_BYTE)
#elif defined(BOOST_ENDIAN_LITTLE_BYTE)
#elif defined(BOOST_ENDIAN_LITTLE_WORD)
...
#endif
Более подробную информацию можно найти в
Обнаружение является консервативным в том смысле, что оно идентифицирует только тот порядок следования байтов, который известен наверняка. В частности, двунаправленность байтов не указывается, так как практически невозможно определить порядок следования байтов из чего-либо, кроме заголовка, предоставленного операционной системой. И известные в настоящее время заголовки не определяют, что доступна программная двойственность байтов.
C Код для проверки, является ли система прямым или старшим индийским.
int i = 7;
char* pc = (char*)(&i);
if (pc[0] == '\x7') // aliasing through char is ok
puts("This system is little-endian");
else
puts("This system is big-endian");
Мой ответ не тот, о котором спрашивают, но действительно ли просто определить, является ли ваша система прямым или прямым порядком байтов?
Код:
#include<stdio.h>
int main()
{
int a = 1;
char *b;
b = (char *)&a;
if (*b)
printf("Little Endian\n");
else
printf("Big Endian\n");
}
Спокойно поздно, но... Если вам абсолютно необходимо иметь макрос и ультрапортативный код, обнаружите и настройте его из встроенной среды (cmake/autotools).
Вот простая программа, чтобы сделать это, подходящая для grepping:
#if __STDC_VERSION__ < 199901L
#error "Requires C99 compatibility"
#endif
#include <stdint.h>
#include <stdio.h>
const char MAGIC[4] = {0xDE, 0xAD, 0xBE, 0xEF};
int
main(void) {
uint32_t magical = *(const uint32_t *)MAGIC;
switch(magical) {
case 0xEFBEADDE: printf("little\n"); break;
case 0xDEADBEEF: printf("big\n"); break;
case 0xADDEEFBE: printf("pdp\n"); break;
default: for(; magical; magical >>= 8) {
switch(magical & 0xff) {
case 0xDE: printf("3"); break;
case 0xAD: printf("2"); break;
case 0xBE: printf("1"); break;
default: printf("0"); }
} printf("\n");}
return (0);
}
Макрос, чтобы найти endiannes
#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))
или же
#include <stdio.h>
#define ENDIAN() { \
volatile unsigned long ul = 1;\
volatile unsigned char *p;\
p = (volatile unsigned char *)&ul;\
if (*p == 1)\
puts("Little endian.");\
else if (*(p+(sizeof(unsigned long)-1)) == 1)\
puts("Big endian.");\
else puts("Unknown endian.");\
}
int main(void)
{
ENDIAN();
return 0;
}