在C ++程序中以编程方式检测字节序
有没有一个程序化的方法来检测你是否在大端或小端架构? 我需要能够编写将在Intel或PPC系统上执行的代码,并使用完全相同的代码(即无条件编译)。
我不喜欢基于types双击的方法 – 它通常会被编译器警告。 这正是工会所要做的!
int is_big_endian(void) { union { uint32_t i; char c[4]; } bint = {0x01020304}; return bint.c[0] == 1; }
这个原则相当于其他人提出的types案例,但是这个更清晰 – 根据C99,保证是正确的。 与直接指针转换相比,gcc更喜欢这个。
这比在编译时修复字节序要好得多 – 对于支持多种体系结构的操作系统(例如Mac os上的胖二进制文件),这对于ppc / i386都是适用的,否则很容易把它弄乱。
你可以通过设置一个int和遮蔽位来实现,但最简单的方法是使用内置的networking字节转换操作(因为networking字节顺序总是大的)。
if ( htonl(47) == 47 ) { // Big endian } else { // Little endian. }
位摆弄可能会更快,但这种方式很简单,直接,不可能搞砸。
请看这篇文章 :
这里有一些代码来确定你的机器是什么types的
int num = 1; if(*(char *)&num == 1) { printf("\nLittle-Endian\n"); } else { printf("Big-Endian\n"); }
这通常是在编译时(通过性能原因)使用编译器提供的头文件或者创build自己的文件来完成的。 在Linux上你有头文件“/usr/include/endian.h”
Ehm …让我感到惊讶的是,没有人意识到编译器会简单地优化testing,并将固定的结果作为返回值。 这呈现了上面的所有代码示例,实际上是无用的。 唯一会返回的是编译时的字节顺序! 是的,我testing了以上所有的例子。 这是MSVC 9.0(Visual Studio 2008)的一个例子。
纯C代码
int32 DNA_GetEndianness(void) { union { uint8 c[4]; uint32 i; } u; ui = 0x01020304; if (0x04 == uc[0]) return DNA_ENDIAN_LITTLE; else if (0x01 == uc[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 : ui = 1; ; 18 : ; 19 : if (1 == uc[0]) ; 20 : return DNA_ENDIAN_LITTLE; mov eax, 1 ; 21 : else if (1 == uc[3]) ; 22 : return DNA_ENDIAN_BIG; ; 23 : else ; 24 : return DNA_ENDIAN_UNKNOWN; ; 25 : } ret _DNA_GetEndianness ENDP END
也许有可能closures任何编译时优化只是这个function,但我不知道。 否则,也许可以在汇编中对其进行硬编码,尽pipe这不是可移植的。 即使如此,甚至可能会得到优化。 这让我觉得我需要一些非常糟糕的汇编器,为所有现有的CPU /指令集实现相同的代码,以及….没关系。
另外,这里有人说在运行时endianness不会改变。 错误。 那里有双端机器。 他们的sorting可能会影响执行。 此外,不仅有小端和大端,而且还有其他的字节序(什么字)。
我讨厌和喜欢编码在同一时间…
声明一个intvariables:
int variable = 0xFF;
现在使用char *指针指向它的各个部分,并检查这些部分是什么。
char* startPart = reinterpret_cast<char*>( &variable ); char* endPart = reinterpret_cast<char*>( &variable ) + sizeof( int ) - 1;
取决于哪一个指向0xFF字节现在你可以检测字节序。 这需要sizeof(int)> sizeof(char),但是对于所讨论的平台来说绝对是正确的。
我惊讶没有人提到预处理器默认定义的macros。 虽然这些将根据您的平台而有所不同, 他们比写自己的endian-check要干净得多。
例如; 如果我们看一下GCC定义的内置macros(在X86-64机器上):
:| gcc -dM -E -xc - |grep -i endian #define __LITTLE_ENDIAN__ 1
在PPC机上,我得到:
:| gcc -dM -E -xc - |grep -i endian #define __BIG_ENDIAN__ 1 #define _BIG_ENDIAN 1
( :| gcc -dM -E -xc -
magic打印出所有内置的macros)。
有关更多详细信息,您可能需要查看此代码项目文章有关Endianness的基本概念 :
如何在运行时dynamictestingEndiantypes?
正如“计算机animation常见问题解答”中所解释的那样,您可以使用以下函数来查看您的代码是在Little-或Big-Endian系统上运行的:Collapse
#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位整数。 字符指针然后被分配指向整数值的第一个(最低有效)字节。 如果整数的第一个字节是0x01h,那么系统是Little-Endian(0x01h在最低或最低有效地址中)。 如果是0x00h那么系统是Big-Endian。
如上所述,使用联合技巧。
上面提到的问题几乎没有什么问题,最值得注意的是对于大多数体系结构来说,未alignment的内存访问速度非常缓慢,除非字alignment,否则一些编译器甚至不会识别这样的常量谓词。
因为仅仅是endiantesting是无聊的,所以这里(模板)函数会根据你的规范翻转任意整数的input/输出,而不pipe主机的体系结构如何。
#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)
要从主机endian转换为给定endian,请使用:
output = endian(hostsource, endian_you_want_to_output)
由此产生的代码与在clang上编写手部程序一样快,在gcc上它比较慢(展开&,<<,>>,|为每个字节),但仍然体面。
如果你有权访问C ++ 20,你可以使用std::endian
:
#include <type_traits> if (std::endian::native == std::endian::big) { // Big endian system } else if (std::endian::native == std::endian::little) { // Little endian system } else { // Something else }
除非您使用的是已经移植到PPC和Intel处理器的框架,否则您将不得不进行条件编译,因为PPC和Intel平台具有完全不同的硬件体系结构,stream水线,总线等。这使得汇编代码完全不同他们俩。
至于寻找endianness,请执行以下操作:
short temp = 0x1234; char* tempChar = (char*)&temp;
您将得到tempChar为0x12或0x34,从中你将知道endianness。
我会做这样的事情:
bool isBigEndian() { static unsigned long x(1); static bool result(reinterpret_cast<unsigned char*>(&x)[0] == 0); return result; }
沿着这些线,你会得到一个时间有效的function,只做一次计算。
bool isBigEndian() { static const uint16_t m_endianCheck(0x00ff); return ( *((uint8_t*)&m_endianCheck) == 0x0); }
编译时,非macros,C ++ 11 constexpr解决scheme:
union { uint16_t s; unsigned char c[2]; } constexpr static d {1}; constexpr bool is_little_endian() { return dc[0] == 1; }
union { int i; char c[sizeof(int)]; } x; xi = 1; if(xc[0] == 1) printf("little-endian\n"); else printf("big-endian\n");
这是另一个解决scheme。 类似于安德鲁·黑尔的解决scheme。
未经testing,但在我看来,这应该工作? 导致它在小端上是0x01,在大端上是0x00?
bool runtimeIsLittleEndian(void) { volatile uint16_t i=1; return ((uint8_t*)&i)[0]==0x01;//0x01=little, 0x00=big }
C ++的方法是使用boost ,其中预处理器检查和强制转换在非常彻底testing的库中进行划分。
Predef库(boost / predef.h)可以识别四种不同的字节顺序 。
Endian库计划提交给C ++标准,并支持对端到端敏感数据的各种操作。
你也可以通过预处理器使用类似boost头文件这样的工具来实现这个function
int i=1; char *c=(char*)&i; bool littleendian=c;
这个怎么样?
#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; }
除非endian头是GCC-only,它提供了可以使用的macros。
#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!"); ...
如果你不想有条件的编译,你可以直接编写endian独立代码。 下面是一个例子(摘自Rob Pike ):
以endian独立的方式读取存储在磁盘上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层代码插图。
// 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; }
这是另一个C版本。 它定义了一个名为wicked_cast()
的macros,用于通过C99联合文本和非标准的__typeof__
运算符进行内联types的__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); }
如果整数是单字节值,则sorting是没有意义的,并且会产生编译时错误。
C编译器(至less我认识的每个人)工作的字节顺序必须在编译时决定。 即使对于biendian处理器(如ARM和MIPS),您也必须在编译时selectendianness。 此外,所有通用文件格式中的可执行文件(如ELF)都定义了字节序。 虽然有可能制作一个二进制代码的二进制块(对于某些ARM服务器的利用可能?),但可能需要在汇编中完成。
我正在阅读教科书: 计算机系统:一个程序员的angular度 ,并有一个问题,以确定哪个字节是由C程序。
我使用指针的function来做到这一点如下:
#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。
正如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不能优化这个代码,即使它优化了其他代码。 我相信这是因为我留下了小字节存储器上的“随机”值(至less有7位),所以编译器无法知道随机值是什么,也不会优化function消失。
我也编写了函数,以便检查只执行一次,并返回值存储下一个testing。