Jay*_*y T 201 c++ algorithm endianness
是否有一种编程方式来检测您是否在大端或小端架构上?我需要能够编写将在Intel或PPC系统上执行的代码并使用完全相同的代码(即没有条件编译).
Dav*_*eau 173
我不喜欢基于类型惩罚的方法 - 它经常会被编译器警告.这正是工会的意义所在!
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
Run Code Online (Sandbox Code Playgroud)
该原则相当于其他人建议的类型情况,但这更清楚 - 根据C99,保证是正确的.与直接指针转换相比,gcc更喜欢这个.
这比在编译时修复字节顺序要好得多 - 对于支持多体系结构的操作系统(例如Mac OS x上的胖二进制文件),这对于ppc/i386都有效,否则很容易搞乱. .
Eri*_*lje 81
您可以通过设置int和屏蔽掉位来实现,但最简单的方法可能就是使用内置的网络字节转换操作(因为网络字节顺序总是大端).
if ( htonl(47) == 47 ) {
// Big endian
} else {
// Little endian.
}
Run Code Online (Sandbox Code Playgroud)
小小的摆弄可能会更快,但这种方式很简单,直截了当,而且非常不可能搞砸.
And*_*are 62
请看这篇文章:
以下是一些确定机器类型的代码
Run Code Online (Sandbox Code Playgroud)int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
Lyb*_*rta 41
std::endian
如果您可以访问C++ 20编译器,例如GCC 8+或Clang 7+,则可以使用:
#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
}
Run Code Online (Sandbox Code Playgroud)
bil*_*ill 36
这通常在编译时完成(特别是出于性能原因),使用编译器提供的头文件或创建自己的头文件.在linux上你有头文件"/usr/include/endian.h"
小智 16
嗯......让我感到惊讶的是,没有人意识到编译器会简单地优化测试,并将固定结果作为返回值.这使得上面的所有代码示例都变得无用.唯一可以返回的是编译时的字节序!是的,我测试了上面的所有例子.以下是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;
}
Run Code Online (Sandbox Code Playgroud)
拆卸
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
Run Code Online (Sandbox Code Playgroud)
也许可以关闭这个函数的任何编译时优化,但我不知道.否则,可能可以在装配中对其进行硬编码,尽管这不是便携式的.即便如此,即使这样也可能会得到优化.这让我觉得我需要一些非常糟糕的汇编程序,为所有现有的CPU /指令集实现相同的代码,而且......没关系.
此外,有人在此表示字节序在运行期间不会改变.错误.那里有双端机器.他们的字节顺序可能因执行而异.此外,不仅有Little Endian和Big Endian,还有其他的endianness(简而言之).
我讨厌并喜欢同时编码......
sha*_*oth 14
声明一个int变量:
int variable = 0xFF;
Run Code Online (Sandbox Code Playgroud)
现在使用char*指针指向它的各个部分并检查这些部分中的内容.
char* startPart = reinterpret_cast<char*>( &variable );
char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;
Run Code Online (Sandbox Code Playgroud)
根据哪一个指向0xFF字节,您可以检测字节顺序.这需要sizeof(int)> sizeof(char),但对于讨论的平台来说肯定是正确的.
Dav*_*veR 14
我很惊讶没有人提到预处理器默认定义的宏.虽然这些将根据您的平台而有所不同; 它们比编写自己的endian-check要清晰得多.
例如; 如果我们看一下GCC定义的内置宏(在X86-64机器上):
:| gcc -dM -E -x c - |grep -i endian
#define __LITTLE_ENDIAN__ 1
Run Code Online (Sandbox Code Playgroud)
在PPC机器上,我得到:
:| gcc -dM -E -x c - |grep -i endian
#define __BIG_ENDIAN__ 1
#define _BIG_ENDIAN 1
Run Code Online (Sandbox Code Playgroud)
(:| gcc -dM -E -x c -
魔法打印出所有内置宏).
有关更多详细信息,您可能需要查看此代码项目文章有关Endianness的基本概念:
如何在运行时动态测试Endian类型?
如计算机动画常见问题解答中所述,您可以使用以下函数来查看您的代码是在Little-还是Big-Endian系统上运行:折叠
Run Code Online (Sandbox Code Playgroud)#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);
}
Run Code Online (Sandbox Code Playgroud)
此代码将值0001h分配给16位整数.然后指定char指针指向整数值的第一个(最低有效)字节.如果整数的第一个字节是0x01h,那么系统是Little-Endian(0x01h是最低或最不重要的地址).如果是0x00h,则系统为Big-Endian.
不要使用union
!
C++ 不允许通过union
s进行类型双关!
从不是最后写入的字段的联合字段读取是未定义的行为!
许多编译器支持这样做作为扩展,但语言不保证。
有关更多详细信息,请参阅此答案:
只有两个有效的答案可以保证是可移植的。
如果您有权访问支持 C++20 的系统,第一个答案
是std::endian
从头<type_traits>
文件中使用。
(在撰写本文时,C++20 尚未发布,但除非发生影响std::endian
包含的事情,否则从 C++20 开始,这将是在编译时测试字节序的首选方法。)
constexpr bool is_little_endian = (std::endian::native == std::endian::little);
Run Code Online (Sandbox Code Playgroud)
在 C++20 之前,唯一有效的答案是存储一个整数,然后通过类型双关检查它的第一个字节。
与union
s的使用不同,这是 C++ 的类型系统明确允许的。
同样重要的是要记住,为了获得最佳的可移植性,static_cast
应该使用它,
因为它reinterpret_cast
是实现定义的。
如果程序尝试通过以下类型之一以外的泛左值访问对象的存储值,则行为未定义:... a
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;
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
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);
}
Run Code Online (Sandbox Code Playgroud)
除非您使用已移植到PPC和Intel处理器的框架,否则您将不得不进行条件编译,因为PPC和Intel平台具有完全不同的硬件架构,管道,总线等.这使得汇编代码在两者之间完全不同.他们俩.
至于查找字节序,请执行以下操作:
short temp = 0x1234;
char* tempChar = (char*)&temp;
Run Code Online (Sandbox Code Playgroud)
您将获得tempChar为0x12或0x34,您将从中知道字节序.
如上所述,使用联合技巧.
上面提到的问题几乎没有问题,最值得注意的是,对于大多数架构来说,未对齐的内存访问是非常慢的,并且一些编译器根本不会识别这样的常量谓词,除非字对齐.
因为光端测试很无聊,所以这里有(模板)函数,它将根据你的规范翻转任意整数的输入/输出,而不管主机架构如何.
#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;
};
Run Code Online (Sandbox Code Playgroud)
用法:
要从给定的endian转换为host,请使用:
host = endian(source, endian_of_source)
要从host endian转换为给定的endian,请使用:
output = endian(hostsource, endian_you_want_to_output)
结果代码与在clang上编写手工组件一样快,在gcc上它的速度稍慢(展开&,<<,>>,对于每个字节)但仍然不错.
bool isBigEndian()
{
static const uint16_t m_endianCheck(0x00ff);
return ( *((uint8_t*)&m_endianCheck) == 0x0);
}
Run Code Online (Sandbox Code Playgroud)