宏定义来确定大端机还是小端机?

是否有一行宏定义来确定机器的最优性?我正在使用下面的代码,但是将它转换为宏会太长:

unsigned char test_endian( void )
{
int test_var = 1;
unsigned char *test_endian = (unsigned char*)&test_var;


return (test_endian[0] == 0);
}
123362 次浏览

使用内联函数而不是宏。此外,您需要在内存中存储某些内容,这是宏的一个不太好的副作用。

You could convert it to a short macro using a static or global variable, like this:

static int s_endianess = 0;
#define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)

虽然没有标准,但是在许多系统上,包括 <endian.h>,都会提供一些需要查找的定义。

为了在运行时检测 endianness,您必须能够引用内存。如果遵循标准 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宏了。

虽然没有可移植的 # Definition 或其他可依赖的东西,但是平台确实提供了转换到和从“主机”端点转换的标准功能。

通常,使用“ network endian”(很大 endian)进行磁盘存储或网络存储,使用主机 endian (在 x86上是 一点点 endian)进行本地计算。您可以使用 htons()ntohs()以及朋友在两者之间进行转换。

如果只想依赖预处理器,则必须计算出预定义符号的列表。预处理器算法没有寻址的概念。

GCC在麦克身上定义 __LITTLE_ENDIAN____BIG_ENDIAN__

$ gcc -E -dM - < /dev/null |grep ENDIAN
#define __LITTLE_ENDIAN__ 1

然后,您可以添加更多的基于平台检测的预处理器条件指令,如 #ifdef _WIN32等。

试试这个:

#include<stdio.h>
int x=1;
#define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian")
int main()
{


TEST;
}

如果您有一个支持 C99复合文字的编译器:

#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})

或:

#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)

但通常,您应该尝试编写不依赖于主机平台的 endianness 的代码。


独立于主机的 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];
}

支持任意字节顺序的代码,准备放入名为 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
#include <stdint.h>
#define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8)
#define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)

我相信这就是我们想要的。 我只在 msvc 下的 Endian 机器上测试过。 请确认一下 Big Endian 机器。

    #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) { ... }中的代码,并用 foo();代替像 if (true) { foo(); }这样的代码。最糟糕的情况是: 编译器不进行优化,你仍然得到正确的代码,但是速度有点慢。

实际上,can通过使用复合文字(C99)访问临时对象的内存:

#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})

Which GCC will evaluate at compile time.

“ C 网络库”提供了处理 endian‘ ness 的函数。即 htons () ,htonl () ,ntohs ()和 ntohl () ... 其中 n 是“ network”(即。而 h 是“ host”(即。运行代码的机器的端点性)。

这些明显的“函数”(通常)被定义为宏(参见 < netinet/in.h >) ,因此使用它们没有运行时开销。

下面的宏使用这些“函数”来评估 endian‘ ness。

#include <arpa/inet.h>
#define  IS_BIG_ENDIAN     (1 == htons(1))
#define  IS_LITTLE_ENDIAN  (!IS_BIG_ENDIAN)

In addition:

我唯一需要知道系统的 endian’ness 的时候是当我写出一个变量[到一个文件/其他文件]时,这个变量可能被另一个未知的 endian’ness 系统读入(为了跨平台兼容性) ... 在这种情况下,你可能更喜欢直接使用 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);

宏去找 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;
}

My answer is not as asked but It is really simple to find 如果你的系统是 little endian 还是 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");
}

不要忘记,endianness 并不是故事的全部—— char的大小可能不是8位(例如 DSP 的) ,不能保证两个补码的否定(例如 Cray) ,可能需要严格的对齐(例如 SPARC,当未对齐时也需要 ARM 弹簧到 中间结尾) ,等等。

将目标对准特定的 CPU architecture可能是一个更好的主意。

例如:

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

不幸的是,这个解决方案也不是超级可移植的,因为它依赖于特定于编译器的定义(没有标准,但是 这是是这些定义的一个很好的编译)。

如果您正在寻找编译时测试并且正在使用 gcc,那么您可以:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__

See 海湾合作委员会文件 for more information.

检查系统是 little-endian 还是 big-indian 的代码。

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");

请注意,这里的大多数答案都是不可移植的,因为现在的编译器会在编译时评估这些答案(取决于优化) ,并根据特定的优化返回一个特定的值,而实际的机器优化可能会有所不同。测试 endianness 的值永远不会到达系统内存,因此实际执行的代码将返回相同的结果,而与实际的 endianness 无关。

对于 例子,在 ARM Cortex-M3中实现的 endianness 将反映在状态位 AIRCR.ENDIANNESS 中,编译器在编译时不能知道这个值。

这里建议的一些答案的汇编结果如下:

https://godbolt.org/z/GJGNE2 for 这个 answer,

Https://godbolt.org/z/yv-pyj 为 this的答案,等等。

要解决这个问题,您需要使用 volatile限定符。Yogeesh H T的答案是现实生活中最接近的答案,但由于 Christoph提出了更全面的解决方案,稍微修改一下他的 回答就可以使答案完整,只需在联合声明中加上 volatile: static const volatile union

这将确保从内存中存储和读取,这是确定 endianness 所需的。

If you dump the preprocessor #defines

gcc -dM -E - < /dev/null
g++ -dM -E -x c++ - < /dev/null

通过编译时间逻辑,你通常可以找到对你有帮助的东西。

#define __LITTLE_ENDIAN__ 1
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__

不过,各种编译器可能有不同的定义。

如果您的编译器支持复合文本,并且您明显不使用 C + + ,那么您可以使用

#define BIG_ENDIAN      ((*(const char*)&(const int){0x01020304}) == 0x01)
#define LITTLE_ENDIAN   ((*(const char*)&(const int){0x01020304}) == 0x04)

这不需要声明任何运行时变量,我认为这使得它比大多数其他解决方案更加简洁

这个问题对于 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

如果 加速可用,那么您可以使用 Boost.Predef,它包含各种针对目标平台的预定义宏,包括 endianness (BOOST_ENDIAN_*)。是的,加速通常被认为是一个 C + + 库,但是这个是一个预处理器头,也可以和 C 一起工作!它允许你在 编译时中便携地检测端点

这个库定义了一组编译器、体系结构、操作系统、库和其他版本号,它可以从 C、 C + + 、 Objective C 和 Objective C + + 预定义的宏或在通常可用的头中定义的宏中收集信息。这个库的想法源于一个提议,即扩展 Boost Config 库,以提供比它所支持的特性定义更多、更一致的信息。下面是这个简短建议的编辑版本。

For example

#include <boost/predef.h>
// or just include the necessary header
// #include <boost/predef/other/endian.h>


#if   BOOST_ENDIAN_BIG_BYTE
#elif BOOST_ENDIAN_LITTLE_BYTE
#elif BOOST_ENDIAN_LITTLE_WORD
...
#endif

更多细节可以在 BOOST_ENDIAN_*中找到

戈德博尔特的演示


请注意,它显然不能检测到在 运行时间期间可以更改端点的双端点平台

检测是保守的,因为它只识别确切知道的 endianness。特别是没有指出双向 endianness,因为除了提供头的操作系统之外,实际上不可能确定任何东西的 endianness。而且当前已知的头并没有定义可用的编程式双优化。