是否有一种编程方式来检测您是否处于big-endian或little-endian架构?我需要能够编写将在Intel或PPC系统上执行的代码并使用完全相同的代码(即没有条件编译)。
答案 0 :(得分:163)
我不喜欢基于类型惩罚的方法 - 它经常会被编译器警告。这正是工会的目的!
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都有效,但很容易弄乱事情不然。
答案 1 :(得分:79)
您可以通过设置int和屏蔽off位来实现,但最简单的方法可能就是使用内置的网络字节转换操作(因为网络字节顺序总是大端)。
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
比特摆弄可能会更快,但这种方式很简单,直截了当,而且非常不可能搞砸。
答案 2 :(得分:60)
请参阅this article:
以下是一些确定内容的代码 您的机器类型
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
答案 3 :(得分:33)
这通常在编译时完成(特别是出于性能原因),使用编译器提供的头文件或创建自己的头文件。在linux上你有头文件“/usr/include/endian.h”
答案 4 :(得分:31)
如果您可以访问C ++ 20编译器(如GCC 8+或Clang 7 +),则可以使用std::endian
:
#include <type_traits>
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 :(得分:15)
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
也许可以关闭这个函数的任何编译时优化,但我不知道。否则,可能可以在装配中对其进行硬编码,尽管这不是便携式的。即便如此,即使这样也可能会得到优化。它让我觉得我需要一些非常糟糕的汇编程序,为所有现有的CPU /指令集实现相同的代码,而且......没关系。
此外,有人在此表示字节顺序在运行时不会改变。错误。那里有双端机器。他们的字节顺序可能因执行而异。此外,不仅有Little Endian和Big Endian,还有其他的endianness(简而言之)。
我讨厌并喜欢同时编码...
答案 6 :(得分:14)
声明一个int变量:
int variable = 0xFF;
现在使用char *指针指向它的各个部分并检查这些部分中的内容。
char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;
根据哪一个指向0xFF字节,您可以检测字节顺序。这需要sizeof(int)&gt; sizeof(char),但对于讨论的平台来说肯定是正确的。
答案 7 :(得分:14)
我很惊讶没有人提到预处理器默认定义的宏。虽然这些将根据您的平台而有所不同;它们比编写自己的endian-check要清晰得多。
例如;如果我们看一下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 -
魔术打印出所有内置宏。)
答案 8 :(得分:8)
有关详细信息,您可以查看此代码项目文章Basic concepts on Endianness:
如何在运行时动态测试Endian类型?
如计算机中所述 动画常见问题,你可以使用 以下函数查看您的代码 正在小端或大端上运行 system:折叠
#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分配给a 16位整数。然后是一个char指针 分配到第一个点 (最不重要的)字节 整数值。如果是第一个字节 整数是0x01h,然后是系统 是Little-Endian(0x01h在 最低或最不重要的 地址)。如果它是0x00h那么 系统是Big-Endian。
答案 9 :(得分:6)
C ++的方法是使用boost,其中预处理器检查和强制转换被分隔在经过深度测试的库中。
Predef库(boost / predef.h)识别four different kinds of endianness。
Endian Library计划提交给C ++标准,并支持对字节序敏感数据的各种操作。
如上面的答案中所述,Endianness将是c ++ 20的一部分。
答案 10 :(得分:5)
除非您使用已移植到PPC和Intel处理器的框架,否则您将不得不进行条件编译,因为PPC和Intel平台具有完全不同的硬件架构,管道,总线等。这将呈现汇编代码两者完全不同。
至于查找字节序,请执行以下操作:
short temp = 0x1234;
char* tempChar = (char*)&temp;
您将获得tempChar为0x12或0x34,您将从中知道字节序。
答案 11 :(得分:5)
如上所述,使用联合技巧。
上面建议的问题很少,最值得注意的是,对于大多数架构来说,未对齐的内存访问速度非常慢,而且一些编译器根本不会识别这样的常量谓词,除非字对齐。
因为光端测试很无聊,所以这里有(模板)函数,它将根据您的规范翻转任意整数的输入/输出,而不管主机架构如何。
#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)
要从host endian转换为给定的endian,请使用:
output = endian(hostsource, endian_you_want_to_output)
结果代码与在clang上编写手工组件一样快,在gcc上它的速度稍慢(展开&amp;,&lt;&lt;,&gt;,>,每个字节),但仍然不错。
答案 12 :(得分:4)
bool isBigEndian()
{
static const uint16_t m_endianCheck(0x00ff);
return ( *((uint8_t*)&m_endianCheck) == 0x0);
}
答案 13 :(得分:4)
我会做这样的事情:
bool isBigEndian() {
static unsigned long x(1);
static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
return result;
}
沿着这些方向,你会得到一个只能进行一次计算的时间效率函数。
答案 14 :(得分: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的解决方案类似。
答案 15 :(得分:3)
宣告:
编译时,非宏,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;
}
答案 16 :(得分:3)
bool runtimeIsLittleEndian(void)
{
volatile uint16_t i=1;
return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big
}
答案 17 :(得分:2)
你也可以通过预处理器使用类似升级头文件的东西来做到这一点boost endian
答案 18 :(得分:2)
如果您不想要条件编译,您可以只编写字节序独立代码。以下是一个示例(摘自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
答案 19 :(得分:1)
int i=1;
char *c=(char*)&i;
bool littleendian=c;
答案 20 :(得分:1)
除非endian标头仅为GCC,否则它会提供您可以使用的宏。
#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!");
...
答案 21 :(得分:1)
这个怎么样?
#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 :(得分:0)
请参阅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;
}
答案 23 :(得分:0)
C编译器(至少我认识的每个人)在编译时决定使用字符串 的方式。即使对于biendian处理器(如ARM och MIPS),您也必须在编译时选择字节序。此外,字节序在可执行文件(例如ELF)的所有常见文件格式中定义。虽然可以制作二进制blob的biandian代码(对某些ARM服务器漏洞可能?),但可能必须在汇编中完成。
答案 24 :(得分: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);
}
如果整数是单字节值,则字节顺序没有意义,并且将生成编译时错误。
答案 25 :(得分: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;
}
答案 26 :(得分:0)
请勿使用union
!
C ++不允许通过union
进行类型修饰!
从不是最后写入的并集字段读取是未定义行为!
许多编译器都支持将其作为扩展,但是这种语言不能保证。
有关更多详细信息,请参见此答案:
https://stackoverflow.com/a/11996970
只有两个有效答案可以保证可移植。
第一个答案,如果您有权访问支持C ++ 20的系统,
是使用std::endian
标头中的<type_traits>
。
(在撰写本文时,C ++ 20尚未发布,但是除非碰巧影响std::endian
的包含,否则这将是在C编译时测试字节序的首选方法。 ++ 20以上。)
constexpr bool is_little_endian = (std::endian::native == std::endian::little);
在C ++ 20之前,唯一有效的答案是存储一个整数,然后通过punning类型检查其第一个字节。
与union
的使用不同,C ++的类型系统明确允许这样做。
请记住,为了获得最佳的可移植性,static_cast
应该很重要,
因为reinterpret_cast
是实现定义的。
如果程序尝试通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义: ...
char
或unsigned char
类型。
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;
}
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);
}
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);
}
答案 27 :(得分:-1)
我正在阅读教科书:计算机系统:程序员的观点,并且有一个问题需要通过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 。因此,如果计算机是小端,那么 char char指针指向值为1,否则,其值应为0.
答案 28 :(得分:-1)
正如Coriiander所指出的,这里的大多数(如果不是全部)代码都会在编译时被优化掉,因此生成的二进制文件不会在运行时检查“endianness”。
据观察,给定的可执行文件不应该以两个不同的字节顺序运行,但是我不知道是否总是这样,并且在编译时检查它似乎是一个黑客攻击。所以我编写了这个函数:
#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个位),因此编译器无法知道该随机值是什么,并且它不会优化功能远离。
我还对函数进行了编码,以便仅执行一次检查,并为下一次测试存储返回值。