有没有一种程序化的方法来检测您是采用大端还是小端架构?我需要能够编写将在Intel或PPC系统上执行的代码,并使用完全相同的代码(即无条件编译)。
有没有一种程序化的方法来检测您是采用大端还是小端架构?我需要能够编写将在Intel或PPC系统上执行的代码,并使用完全相同的代码(即无条件编译)。
Answers:
我不喜欢基于punning类型的方法-编译器经常会警告它。这正是工会的宗旨!
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
该原理与其他人所建议的类型情况相同,但这更加清楚-根据C99,可以保证它是正确的。与直接指针强制转换相比,gcc更喜欢这种方式。
这也比在编译时固定字节序要好得多-对于支持多体系结构的OS(例如,Mac os x上的胖二进制文件),这对ppc / i386都适用,否则很容易搞乱其他情况。
CHAR_BIT != 8
呢?
您可以通过设置int并掩盖位来完成此操作,但是最简单的方法可能只是使用内置的网络字节转换操作(因为网络字节顺序始终是大字节序)。
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
位修改可能会更快,但是这种方式简单,直接并且几乎不可能搞乱。
BSWAP
操作的微体系结构,则尤其如此。
请看这篇文章:
这是一些代码来确定您的机器类型是什么
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
如果可以std::endian
访问C ++ 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
}
通常在编译时(特别是出于性能原因)使用编译器提供的头文件或创建自己的头文件来完成此操作。在Linux上,您具有头文件“ /usr/include/endian.h”
令我惊讶的是,没有人提到预处理器默认定义的宏。尽管这些将取决于您的平台;它们比编写自己的字节序检查要干净得多。
例如; 如果我们看一下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 -
魔术会打印出所有内置宏)。
echo "\n" | gcc -x c -E -dM - |& grep -i 'endian'
不会返回任何内容,而/usr/sfw/bin
Solaris中的gcc 3.4.3(无论如何)都具有以下定义。我在VxWorks Tornado(gcc 2.95)-vs- VxWorks Workbench(gcc 3.4.4)上看到了类似的问题。
嗯……令我惊讶的是,没有人意识到编译器会简单地优化测试,并将固定的结果作为返回值。这使得上面的所有代码示例实际上无效。唯一会返回的是编译时的字节序!是的,我测试了所有上述示例。这是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 /指令集实现相同的代码,好吧....没关系。
另外,这里有人说,字节序在运行时不会改变。错误。那里有bi-endian机器。它们的字节顺序可以在执行期间变化。另外,不仅有Little Endian和Big Endian,还有其他endianness(单词)。
我讨厌同时喜欢编码...
有关更多详细信息,您可能需要查看此代码项目文章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。
除非您使用已移植到PPC和Intel处理器的框架,否则您将必须进行条件编译,因为PPC和Intel平台具有完全不同的硬件体系结构,管线,总线等。这使得汇编代码之间完全不同他们俩。
关于查找字节序,请执行以下操作:
short temp = 0x1234;
char* tempChar = (char*)&temp;
您将获得tempChar为0x12或0x34,从中您将知道字节序。
stdint.h
并用于int16_t
将来的证明以防不同。
我会做这样的事情:
bool isBigEndian() {
static unsigned long x(1);
static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0);
return result;
}
沿着这些思路,您将获得一个仅执行一次计算的省时功能。
如上所述,使用联合技巧。
不过,上面建议的问题很少,最值得注意的是,对于大多数体系结构而言,未对齐的内存访问速度非常慢,而且,除非字对齐,否则某些编译器甚至根本不会识别这种常量谓词。
由于仅字节序测试很无聊,因此这里使用了(模板)函数,该函数将根据您的规范翻转任意整数的输入/输出,而与主机体系结构无关。
#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上则稍慢一些(展开的&,<<,>>,|每个字节),但仍然不错。
不要使用union
!
C ++不允许通过union
s 进行类型修饰!
从不是最后写入的并集字段读取是未定义的行为!
许多编译器都支持将其作为扩展,但是这种语言不能保证。
查看此答案以获取更多详细信息:
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
s 不同,C ++的类型系统明确允许这样做。
同样重要的是要记住,最佳的便携性static_cast
,应使用,
因为reinterpret_cast
是实现定义。
如果程序尝试通过除以下类型之一以外的glvalue来访问对象的存储值,则行为未定义:... a
char
或unsigned char
type。
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);
}
宣布:
编译时,非宏,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;
}
您还可以通过预处理器使用boost标头文件(可以在boost endian中找到)进行此操作
除非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!");
...
__BYTE_ORDER__
,__ORDER_LITTLE_ENDIAN__
和__ORDER_BIG_ENDIAN__
?
如果您不希望条件编译,则可以编写字节序独立的代码。这是一个示例(摘自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
这是另一个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);
}
如果整数是单字节值,则字节顺序没有意义,并且会生成编译时错误。
正如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位),因此编译器无法知道该随机值是什么,并且不会进行优化功能消失了。
我还对该函数进行了编码,以使检查仅执行一次,并且返回值存储用于下次测试。
0x7FE
?为什么要使用malloc()
呢?那是浪费。而且_BE
是(尽管很小的)内存泄漏和争用情况等待发生,动态缓存结果的好处是不值得的。我将static const uint16_t teste = 1; int is_little_endian() { return (0x01 == ((uint8_t*)&teste)[0]); } int is_big_endian() { return (0x01 == ((uint8_t*)&teste)[1]); }
改为执行以下操作:简单有效,并且在运行时执行的工作要少得多。
volatile
或#pragma
等等
请参见字节序 -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;
}
我正在阅读教科书:“ 计算机系统:程序员的观点”,而用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个字节。我们可以用一个字符指针,以指向INT值为1。因此,如果计算机是小端的字符是字符指针指向的值为1,否则,其值应为0。