Cmacros定义来确定大端还是小端?
是否有一行macros定义来确定机器的字节顺序。 我正在使用下面的代码,但将其转换为macros将太长。
unsigned char test_endian( void ) { int test_var = 1; unsigned char test_endian* = (unsigned char*)&test_var; return (test_endian[0] == NULL); }
支持任意字节顺序的代码,可以放入一个名为order32.h
的文件中:
#ifndef ORDER32_H #define ORDER32_H #include <limits.h> #include <stdint.h> #if CHAR_BIT != 8 #error "unsupported char size" #endif enum { O32_LITTLE_ENDIAN = 0x03020100ul, O32_BIG_ENDIAN = 0x00010203ul, O32_PDP_ENDIAN = 0x01000302ul }; static const union { unsigned char bytes[4]; uint32_t value; } o32_host_order = { { 0, 1, 2, 3 } }; #define O32_HOST_ORDER (o32_host_order.value) #endif
你会通过检查小端系统
O32_HOST_ORDER == O32_LITTLE_ENDIAN
如果你有一个支持C99复合文字的编译器:
#define IS_BIG_ENDIAN (!*(unsigned char *)&(uint16_t){1})
要么:
#define IS_BIG_ENDIAN (!(union { uint16_t u16; unsigned char c; }){ .u16 = 1 }.c)
一般来说,您应该尝试编写不依赖于主机平台的字节序的代码。
独立于host-endian的ntohl()
实现示例:
uint32_t ntohl(uint32_t n) { unsigned char *np = (unsigned char *)&n; return ((uint32_t)np[0] << 24) | ((uint32_t)np[1] << 16) | ((uint32_t)np[2] << 8) | (uint32_t)np[3]; }
没有标准,但包括<endian.h>
在内的很多系统都会给你一些定义来寻找。
要在运行时检测字节序,您必须能够引用内存。 如果你坚持标准C语言,在内存中声明一个variables需要一个语句,但是返回一个值需要一个expression式。 我不知道如何在一个macros中做到这一点 – 这就是为什么gcc有扩展名:-)
如果你愿意有一个.h文件,你可以定义
static uint32_t endianness = 0xdeadbeef; enum endianness { BIG, LITTLE }; #define ENDIANNESS ( *(const char *)&endianness == 0xef ? LITTLE \ : *(const char *)&endianness == 0xde ? BIG \ : assert(0))
然后你可以使用ENDIANNESS
macros。
如果只想依靠预处理器,则必须找出预定义符号的列表。 预处理器算术没有寻址的概念。
Mac上的 GCC定义了__LITTLE_ENDIAN__
或__BIG_ENDIAN__
$ gcc -E -dM - < /dev/null |grep ENDIAN #define __LITTLE_ENDIAN__ 1
然后,您可以添加更多的基于平台检测的预处理器条件指令,如#ifdef _WIN32
等
我相信这是要求的。 我只在msvc下的一个小端机上testing过。 有人在大型机器上进行确认。
#define LITTLE_ENDIAN 0x41424344UL #define BIG_ENDIAN 0x44434241UL #define PDP_ENDIAN 0x42414443UL #define ENDIAN_ORDER ('ABCD') #if ENDIAN_ORDER==LITTLE_ENDIAN #error "machine is little endian" #elif ENDIAN_ORDER==BIG_ENDIAN #error "machine is big endian" #elif ENDIAN_ORDER==PDP_ENDIAN #error "jeez, machine is PDP!" #else #error "What kind of hardware is this?!" #endif
作为一个方面说明(编译器特定),用一个积极的编译器,你可以使用“死代码消除”优化来达到与编译时相同的效果#if
是这样的话:
unsigned yourOwnEndianSpecific_htonl(unsigned n) { static unsigned long signature= 0x01020304UL; if (1 == (unsigned char&)signature) // big endian return n; if (2 == (unsigned char&)signature) // the PDP style { n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL); return n; } if (4 == (unsigned char&)signature) // little endian { n = (n << 16) | (n >> 16); n = ((n << 8) & 0xFF00FF00UL) | ((n>>8) & 0x00FF00FFUL); return n; } // only weird machines get here return n; // ? }
上面依赖于编译器在编译时识别常量值的事实,完全删除if (false) { ... }
代码并replaceif (true) { foo(); }
if (true) { foo(); }
与foo();
最糟糕的情况:编译器不做优化,你仍然得到正确的代码,但有点慢。
您实际上可以通过使用复合文字(C99)来访问临时对象的内存:
#define IS_LITTLE_ENDIAN (1 == *(unsigned char *)&(const int){1})
GCC将在编译时进行评估。
'Cnetworking库'提供处理字节序的function。 即htons(),htonl(),ntohs()和ntohl()…,其中n是“networking”(即big-endian),h是“host”(即运行码)。
这些明显的“function”(通常)被定义为macros[见<netinet / in.h>],所以使用它们没有运行开销。
下面的macros使用这些“函数”来评估字节序。
#include <arpa/inet.h> #define IS_BIG_ENDIAN (1 == htons(1)) #define IS_LITTLE_ENDIAN (!IS_BIG_ENDIAN)
此外:
我唯一需要知道一个系统的字节顺序的时候,是我写出一个variables[给另一个文件],这个variables可能被另一个未知的endian的系统读入(为了跨平台的兼容性)…在这些情况下,您可能更喜欢直接使用endian函数:
#include <arpa/inet.h> #define JPEG_MAGIC (('J'<<24) | ('F'<<16) | ('I'<<8) | 'F') // Result will be in 'host' byte-order unsigned long jpeg_magic = JPEG_MAGIC; // Result will be in 'network' byte-order (IE. Big-Endian/Human-Readable) unsigned long jpeg_magic = htonl(JPEG_MAGIC);
使用内联函数而不是macros。 此外,你需要在内存中存储一些macros,这是一个不太好的副作用。
您可以使用静态或全局variables将其转换为短的macros,如下所示:
static int s_endianess = 0; #define ENDIANESS() ((s_endianess = 1), (*(unsigned char*) &s_endianess) == 0)
虽然没有可移植的#define或可依赖的东西,平台确实提供了标准函数来转换你的“主机”端。
一般情况下,你使用'endian'作为BIG endian,使用host endian(在x86上是LITTLE endian)进行本地计算,从而存储磁盘或networking。 你用htons()
和ntohs()
和朋友在两者之间进行转换。
如果你正在寻找一个编译时testing,而你正在使用gcc,你可以这样做:
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
有关更多信息,请参阅gcc文档 。
尝试这个:
#include<stdio.h> int x=1; #define TEST (*(char*)&(x)==1)?printf("little endian"):printf("Big endian") int main() { TEST; }
#include <stdint.h> #define IS_LITTLE_ENDIAN (*(uint16_t*)"\0\1">>8) #define IS_BIG_ENDIAN (*(uint16_t*)"\1\0">>8)
不要忘记,字节序不是整个故事 – char
的大小可能不是8位(例如DSP),二进制补码否定不能保证(例如Cray),可能需要严格的alignment(例如SPARC,也可能是ARM 中间码未alignment)等等。
改为针对特定的CPU架构可能更好。
例如:
#if defined(__i386__) || defined(_M_IX86) || defined(_M_IX64) #define USE_LITTLE_ENDIAN_IMPL #endif void my_func() { #ifdef USE_LITTLE_ENDIAN_IMPL // Intel x86-optimized, LE implementation #else // slow but safe implementation #endif }
请注意,这个解决scheme也不是非常便携,因为它依赖于编译器特定的定义(没有标准,但是这里有一个很好的编译定义)。
我的答案并不像问,但是如果你的系统是小端或大端,这是非常简单的。
码:
#include<stdio.h> int main() { int a = 1; char *b; b = (char *)&a; if (*b) printf("Little Endian\n"); else printf("Big Endian\n"); }
用于检查系统是小端还是大印度的C代码。
int i = 7; char* pc = (char*)(&i); if (pc[0] == '\x7') // aliasing through char is ok puts("This system is little-endian"); else puts("This system is big-endian");
安静的迟到,但是…如果你绝对必须有一个macros和超便携的代码,检测并build立你的内置环境(cmake / autotools)。
这里有一个简单的程序来完成它,适合grepping:
#if __STDC_VERSION__ < 199901L #error "Requires C99 compatibility" #endif #include <stdint.h> #include <stdio.h> const char MAGIC[4] = {0xDE, 0xAD, 0xBE, 0xEF}; int main(void) { uint32_t magical = *(const uint32_t *)MAGIC; switch(magical) { case 0xEFBEADDE: printf("little\n"); break; case 0xDEADBEEF: printf("big\n"); break; case 0xADDEEFBE: printf("pdp\n"); break; default: for(; magical; magical >>= 8) { switch(magical & 0xff) { case 0xDE: printf("3"); break; case 0xAD: printf("2"); break; case 0xBE: printf("1"); break; default: printf("0"); } } printf("\n");} return (0); }
macros查找endiannes
#define ENDIANNES() ((1 && 1 == 0) ? printf("Big-Endian"):printf("Little-Endian"))
要么
#include <stdio.h> #define ENDIAN() { \ volatile unsigned long ul = 1;\ volatile unsigned char *p;\ p = (volatile unsigned char *)&ul;\ if (*p == 1)\ puts("Little endian.");\ else if (*(p+(sizeof(unsigned long)-1)) == 1)\ puts("Big endian.");\ else puts("Unknown endian.");\ } int main(void) { ENDIAN(); return 0; }