在C中转换大端到小端
我需要编写一个函数来将大端转换为C中的小端。我不能使用任何库函数。
假设你需要的是一个简单的字节交换,尝试类似
无符号16位转换:
swapped = (num>>8) | (num<<8);
无符号32位转换:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0 ((num<<8)&0xff0000) | // move byte 1 to byte 2 ((num>>8)&0xff00) | // move byte 2 to byte 1 ((num<<24)&0xff000000); // byte 0 to byte 3
这将字节顺序从位置1234交换到4321.如果您的input是0xdeadbeef
,则32位的endian交换可能具有0xefbeadde
输出。
上面的代码应该用macros或者至less是常量而不是幻数来清理,但是希望它能够帮助你
编辑:作为另一个答案指出,有平台,操作系统和指令集具体的替代品可以比以上更快。 在Linux内核中有一些macros(比如cpu_to_be32),可以很好地处理字节序。 但是这些替代scheme是特定于其环境的。 在实践中,最好使用可用方法的混合来处理字节顺序
通过包括:
#include <byteswap.h>
您可以获得机器相关的字节交换function的优化版本。 然后,您可以轻松使用以下function:
__bswap_32 (uint32_t input)
要么
__bswap_16 (uint16_t input)
#include <stdint.h> //! Byte swap unsigned short uint16_t swap_uint16( uint16_t val ) { return (val << 8) | (val >> 8 ); } //! Byte swap short int16_t swap_int16( int16_t val ) { return (val << 8) | ((val >> 8) & 0xFF); } //! Byte swap unsigned int uint32_t swap_uint32( uint32_t val ) { val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); return (val << 16) | (val >> 16); } //! Byte swap int int32_t swap_int32( int32_t val ) { val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); return (val << 16) | ((val >> 16) & 0xFFFF); }
更新 :增加了64位字节交换
int64_t swap_int64( int64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } uint64_t swap_uint64( uint64_t val ) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | (val >> 32); }
这是一个相当通用的版本; 我没有编译它,所以可能有错别字,但你应该明白,
void SwapBytes(void *pv, size_t n) { char *p = pv; size_t lo, hi; for(lo=0, hi=n-1; hi>lo; lo++, hi--) { char tmp=p[lo]; p[lo] = p[hi]; p[hi] = tmp; } } #define SWAP(x) SwapBytes(&x, sizeof(x));
NB:这不是速度或空间的优化。 它的目的是清楚(易于debugging)和便携式。
如果你需要macros(例如embedded式系统):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8)) #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
作为一个笑话:
#include <stdio.h> int main (int argc, char *argv[]) { size_t sizeofInt = sizeof (int); int i; union { int x; char c[sizeof (int)]; } original, swapped; original.x = 0x12345678; for (i = 0; i < sizeofInt; i++) swapped.c[sizeofInt - i - 1] = original.c[i]; fprintf (stderr, "%x\n", swapped.x); return 0; }
编辑:这些是库函数。 跟着他们是手动的方式来做到这一点。
对于不知道__byteswap_ushort,__byteswap_ulong和__byteswap_uint64的人数,我感到非常震惊。 当然,它们是Visual C ++特有的,但是它们可以在x86 / IA-64体系结构上编译成一些美味的代码。 🙂
下面是从这个页面拉出的bswap
指令的明确用法。 请注意,上面的内在forms总是会比这更快 ,我只是添加它来给出一个没有库例程的答案。
uint32 cq_ntohl(uint32 a) { __asm{ mov eax, a; bswap eax; } }
这个工作会更快吗?
uint32_t swapped, result; ((byte*)&swapped)[0] = ((byte*)&result)[3]; ((byte*)&swapped)[1] = ((byte*)&result)[2]; ((byte*)&swapped)[2] = ((byte*)&result)[1]; ((byte*)&swapped)[3] = ((byte*)&result)[0];
以下是使用SSSE3指令pshufb使用其Intel内部函数的一种方法,假设您有4个int
的倍数:
unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) { int i; __m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3); for (i = 0; i < length; i += 4) { _mm_storeu_si128((__m128i *)&destination[i], _mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask)); } return destination; }
编辑:这个function只交换alignment的16位字的字节顺序。 UTF-16 / UCS-2编码经常需要的function。 编辑结束。
如果你想改变内存块的永久性,你可以使用我非常快速的方法。 你的内存数组的大小应该是8的倍数。
#include <stddef.h> #include <limits.h> #include <stdint.h> void ChangeMemEndianness(uint64_t *mem, size_t size) { uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT; size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t); for(; size; size--, mem++) *mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT); }
这种function对于改变Unicode UCS-2 / UTF-16文件的字节顺序非常有用。
这个代码片段可以将32位小端数转换为大端数。
#include <stdio.h> main(){ unsigned int i = 0xfafbfcfd; unsigned int j; j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24); printf("unsigned int j = %x\n ", j); }
这是我一直在使用的一个function – testing和工作在任何基本的数据types:
// SwapBytes.h // // Function to perform in-place endian conversion of basic types // // Usage: // // double d; // SwapBytes(&d, sizeof(d)); // inline void SwapBytes(void *source, int size) { typedef unsigned char TwoBytes[2]; typedef unsigned char FourBytes[4]; typedef unsigned char EightBytes[8]; unsigned char temp; if(size == 2) { TwoBytes *src = (TwoBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[1]; (*src)[1] = temp; return; } if(size == 4) { FourBytes *src = (FourBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[3]; (*src)[3] = temp; temp = (*src)[1]; (*src)[1] = (*src)[2]; (*src)[2] = temp; return; } if(size == 8) { EightBytes *src = (EightBytes *)source; temp = (*src)[0]; (*src)[0] = (*src)[7]; (*src)[7] = temp; temp = (*src)[1]; (*src)[1] = (*src)[6]; (*src)[6] = temp; temp = (*src)[2]; (*src)[2] = (*src)[5]; (*src)[5] = temp; temp = (*src)[3]; (*src)[3] = (*src)[4]; (*src)[4] = temp; return; } }
如果您在x86或x86_64处理器上运行,则该大端是本地的。 所以
为16位值
unsigned short wBigE = value; unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
为32位值
unsigned int iBigE = value; unsigned int iLittleE = ((iBigE & 0xFF) << 24) | ((iBigE & 0xFF00) << 8) | ((iBigE >> 8) & 0xFF00) | (iBigE >> 24);
这不是最有效的解决scheme,除非编译器认识到这是字节级别的操作并生成字节交换代码。 但它不依赖于任何内存布局的技巧,可以很容易地变成一个macros。