225

有没有一种程序化的方式来检测你是在大端还是小端架构上?我需要能够编写将在 Intel 或 PPC 系统上执行的代码并使用完全相同的代码(即无条件编译)。

4

29 回答 29

179

我不喜欢基于类型双关的方法——编译器经常会警告它。这正是工会的目的!

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 都适用,否则很容易搞砸.

于 2009-06-16T13:08:04.500 回答
86

std::endian如果您可以访问 C++20 编译器,例如 GCC 8+ 或 Clang 7+,则可以使用。

注:std::endian开始于2019 年科隆会议,<type_traits>移至该会议。<bit>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
}
于 2016-07-01T09:11:07.837 回答
85

您可以通过设置一个 int 并屏蔽位来做到这一点,但可能最简单的方法就是使用内置的网络字节转换操作(因为网络字节顺序总是大端)。

if ( htonl(47) == 47 ) {
  // Big endian
} else {
  // Little endian.
}

位摆弄可能会更快,但这种方式简单、直接且几乎不可能搞砸。

于 2009-06-16T13:00:53.500 回答
68

请看这篇文章

这是一些代码来确定您的机器的类型

int num = 1;
if(*(char *)&num == 1)
{
    printf("\nLittle-Endian\n");
}
else
{
    printf("Big-Endian\n");
}
于 2009-06-16T13:00:37.773 回答
42

这通常在编译时完成(特别是出于性能原因),方法是使用编译器提供的头文件或创建您自己的头文件。在 linux 上,你有头文件“/usr/include/endian.h”

于 2009-06-16T13:36:04.113 回答
17

我很惊讶没有人提到预处理器默认定义的宏。虽然这些会因您的平台而异;它们比编写自己的字节序检查要干净得多。

例如; 如果我们查看 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 -魔术会打印出所有内置宏)。

于 2009-06-20T19:15:07.617 回答
15

嗯...令我惊讶的是,没有人意识到编译器只会简单地优化测试,并将固定结果作为返回值。这使得上面的所有代码示例都变得毫无用处。唯一会返回的是编译时的字节序!是的,我测试了上述所有示例。这是一个使用 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

也许可以只为这个函数关闭任何编译时优化,但我不知道。否则,也许可以在汇编中对其进行硬编码,尽管这不是可移植的。即便如此,即使这样也可能会得到优化。这让我觉得我需要一些非常糟糕的汇编程序,为所有现有的 CPU/指令集实现相同的代码,而且……没关系。

另外,这里有人说字节序在运行时不会改变。错误的。那里有双端机器。它们的字节顺序在执行过程中会有所不同。ALSO,不仅有 Little Endian 和 Big Endian,还有其他的字节序(多么好的一个词)。

我既讨厌又喜欢编码……

于 2011-05-04T01:11:06.490 回答
15

声明一个 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 ),但对于所讨论的平台来说绝对是正确的。

于 2009-06-16T13:00:05.577 回答
10

不要使用union

C++ 不允许通过unions!
从不是最后写入的字段的联合字段中读取是未定义的行为
许多编译器支持这样做作为扩展,但语言不保证。

有关更多详细信息,请参阅此答案:

https://stackoverflow.com/a/11996970


只有两个有效的答案可以保证是可移植的。

如果您可以访问支持 C++20 的系统,第一个答案
std::endian从头文件中使用<bit>

C++20 以后

constexpr bool is_little_endian = (std::endian::native == std::endian::little);

在 C++20 之前,唯一有效的答案是存储一个整数,然后通过类型双关语检查它的第一个字节。与使用unions 不同,这是 C++ 的类型系统明确允许的。

同样重要的是要记住,static_cast应该使用最佳可移植性,
因为reinterpret_cast是实现定义的。

如果程序尝试通过非下列类型之一的泛左值访问对象的存储值,则行为未定义: ... acharunsigned 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);
}
于 2019-05-17T17:56:52.453 回答
7

有关更多详细信息,您可能需要查看此代码项目文章有关 Endianness 的基本概念

如何在运行时动态测试 Endian 类型?

如计算机动画常见问题解答中所述,您可以使用以下函数来查看您的代码是在 Little-Endian 还是 Big-Endian 系统上运行:

#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,则系统是 Little-Endian(0x01h 位于最低或最低有效地址)。如果它是 0x00h,那么系统是 Big-Endian。

于 2009-06-16T13:03:53.027 回答
7

C++ 方法一直是使用boost,其中预处理器检查和强制转换在经过彻底测试的库中被分隔开。

Predef 库 (boost/predef.h) 识别四种不同的字节序

Endian Library计划提交给 C++ 标准,并支持对 endian 敏感数据的各种操作。

如上面的答案所述,字节顺序将成为 c++20 的一部分。

于 2015-09-11T01:14:27.000 回答
6

如上所述,使用联合技巧。

不过,上面建议的问题很少,最值得注意的是,对于大多数体系结构而言,未对齐的内存访问速度非常慢,一些编译器甚至根本无法识别这样的常量谓词,除非字对齐。

因为单纯的字节序测试很无聊,所以这里有(模板)函数,它将根据您的规范翻转任意整数的输入/输出,而不管主机架构如何。

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

用法:

要将给定的字节序转换为主机,请使用:

host = endian(source, endian_of_source)

要将主机字节序转换为给定字节序,请使用:

output = endian(hostsource, endian_you_want_to_output)

生成的代码与在 clang 上编写手动汇编一样快,在 gcc 上它有点慢(展开 &,<<,>>,| 对于每个字节)但仍然不错。

于 2012-10-12T21:22:12.423 回答
6

除非您使用已移植到 PPC 和 Intel 处理器的框架,否则您将不得不进行条件编译,因为 PPC 和 Intel 平台具有完全不同的硬件架构、管道、总线等。这使得汇编代码在两者之间完全不同他们俩。

至于查找字节顺序,请执行以下操作:

short temp = 0x1234;
char* tempChar = (char*)&temp;

您将得到 tempChar 为 0x12 或 0x34,从中您将知道字节顺序。

于 2009-06-16T13:00:03.080 回答
6
bool isBigEndian()
{
    static const uint16_t m_endianCheck(0x00ff);
    return ( *((const uint8_t*)&m_endianCheck) == 0x0); 
}
于 2012-11-25T15:58:47.697 回答
4

我会做这样的事情:

bool isBigEndian() {
    static unsigned long x(1);
    static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
    return result;
}

沿着这些思路,您将获得一个只执行一次计算的高效函数。

于 2009-06-16T13:06:54.453 回答
4

未经测试,但在我看来,这应该有效吗?因为它在小端上是 0x01,在大端上是 0x00?

bool runtimeIsLittleEndian(void)
{
 volatile uint16_t i=1;
 return  ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
于 2015-02-14T03:10:03.690 回答
3

宣布:

我最初的帖子被错误地声明为“编译时间”。不是,在当前的 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;
}
于 2014-05-21T04:43:39.550 回答
3
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");

这是另一种解决方案。类似于 Andrew Hare 的解决方案。

于 2012-10-02T10:10:30.047 回答
3

如果您不想要条件编译,您可以编写与字节序无关的代码。这是一个示例(取自Rob Pike):

以与字节序无关的方式读取存储在磁盘上的小字节序的整数:

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
于 2017-02-17T11:58:49.400 回答
2

您也可以通过预处理器使用诸如 boost 头文件之类的东西来执行此操作,该文件可以在boost endian中找到

于 2009-06-16T14:44:48.767 回答
2

除非 endian 标头是 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!");
...
于 2015-04-18T19:08:44.963 回答
1

请参阅字节顺序- C 级代码插图。

// assuming target architecture is 32-bit = 4-Bytes
enum ENDIANNESS{ LITTLEENDIAN , BIGENDIAN , UNHANDLE };


ENDIANNESS 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;
} 
于 2009-06-16T13:00:52.313 回答
1
int i=1;
char *c=(char*)&i;
bool littleendian=c;
于 2009-06-16T13:01:09.553 回答
1

C 编译器(至少我认识的每个人)的工作方式必须在编译时确定字节顺序。即使对于双字节序处理器(如 ARM och MIPS),您也必须在编译时选择字节序。此外,可执行文件(例如 ELF)的所有常见文件格式都定义了字节序。虽然可以制作一个二进制 blob 的 biandian 代码(可能对于某些 ARM 服务器漏洞利用?)它可能必须在汇编中完成。

于 2012-11-25T14:56:32.873 回答
0

这是另一个 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);
}

如果整数是单字节值,字节序就没有意义,并且会产生编译时错误。

于 2009-06-16T17:55:05.337 回答
0

这个怎么样?

#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;
}
于 2009-06-16T13:02:25.523 回答
0

虽然没有快速和标准的方法来确定它,但这将输出它:

#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; 
} 
于 2018-12-27T09:14:16.767 回答
-1

正如 Coriiander 所指出的,这里的大多数(如果不是全部)代码将在编译时进行优化,因此生成的二进制文件不会在运行时检查“字节顺序”。

据观察,给定的可执行文件不应以两种不同的字节顺序运行,但我不知道是否总是如此,而且在编译时检查对我来说似乎是一种黑客行为。所以我编写了这个函数:

#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 位),所以编译器不知道该随机值是什么并且它不会优化功能消失。

我还对函数进行了编码,以便只执行一次检查,并存储返回值以供下次测试使用。

于 2014-09-28T08:46:33.530 回答
-2

我正在阅读教科书:计算机系统:程序员的视角,并且通过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 指针指向值为 1 的int。因此,如果计算机是 little endian,则char指针指向的 char值为 1,否则其值为 0。

于 2013-10-15T11:41:56.560 回答