我需要编写一个将C中的大端转换为小端的函数。我不能使用任何库函数。
我需要编写一个将C中的大端转换为小端的函数。我不能使用任何库函数。
Answers:
假设您需要一个简单的字节交换,请尝试类似
无符号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
这0xdeadbeef
会将字节顺序从位置1234交换到4321。如果您输入的是32位字节的字节序交换,其输出可能为0xefbeadde
。
上面的代码应使用宏或至少常量(而不是幻数)进行清理,但希望它能照搬
编辑:正如另一个答案指出的那样,有特定于平台,操作系统和指令集的替代方案,它们可能比上述方法快得多。在Linux内核中,有一些宏(例如cpu_to_be32)可以很好地处理字节序。但是这些替代方案特定于其环境。在实践中,最好使用多种可用方法来处理字节序
((num & 0xff) >> 8) | (num << 8)
,则gcc 4.8.3会生成一条rol
指令。如果将32位转换写为((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
,则同一编译器将生成一条bswap
指令。
struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
这是一个具有8个字段(每个1位)的位域。但是我不确定那是否与其他建议一样快。对于整数,请使用union { int i; byte_t[sizeof(int)]; }
来逐字节逆转整数。
通过包括:
#include <byteswap.h>
您可以获得与机器有关的字节交换功能的优化版本。然后,您可以轻松使用以下功能:
__bswap_32 (uint32_t input)
要么
__bswap_16 (uint16_t input)
#include <byteswap.h>
,请参阅.h文件本身中的注释。这篇文章包含有用的信息,因此尽管作者忽略了不使用lib函数的OP要求,但我还是投票赞成。
#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);
}
int32_t
和int64_t
变体,对... & 0xFFFF
和进行屏蔽的原因是什么... & 0xFFFFFFFFULL
?我看不到这里是否发生了与符号扩展有关的事情?另外,为什么要swap_int64
返回uint64_t
?那不是int64_t
吗?
swap_int64
答案中返回值的类型。+1有用的答案,顺便说一句!
LL
在不必要(u)swap_uint64()
像很多L
不在需要(u)swap_uint32()
。将U
不需要在uswap_uint64()
很像U
是不是需要uswap_uint32()
这是一个相当通用的版本;我还没有编译它,所以可能有错别字,但是您应该明白这一点,
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
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));
注意:这不是针对速度或空间进行优化的。它旨在保持清晰(易于调试)和便携式。
更新2018-04-04 添加了assert()来捕获n == 0的无效情况,正如评论者@chux所发现的那样。
bswap
由运行良好的X86编译器编译成一条指令。带有尺寸参数的版本无法执行此操作。
如果需要宏(例如嵌入式系统):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
UINT
他们的名字中有。
编辑:这些是库函数。遵循它们是手动操作。
不知道__byteswap_ushort,__byteswap_ulong和__byteswap_uint64的人数令我震惊。当然它们是特定于Visual C ++的,但是它们可以在x86 / IA-64体系结构上编译为一些可口的代码。:)
这是从此页面提取的bswap
指令的显式用法。请注意,上面的固有形式将始终比此形式快,我只是添加了它以给出答案,而没有使用库例程。
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
开个玩笑:
#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;
}
int i, size_t sizeofInt
并且两者都不相同。
这是使用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;
}
这项工作/会更快吗?
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];
char
不是byte
。
这是我一直在使用的功能-经过测试,可在任何基本数据类型上使用:
// 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;
}
}
source
根据需要进行对齐-但是,如果该假设不成立,则代码为UB。
编辑:此函数仅交换对齐的16位字的字节序。UTF-16 / UCS-2编码通常必需的功能。编辑结束。
如果要更改内存块的字节序,可以使用我的快速方法。您的内存阵列的大小应为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);
}
这种功能对于更改Unicode UCS-2 / UTF-16文件的字节序很有用。
t know if it
的建议速度不如建议快,但它确实行得通:github.com/heatblazer/helpers/blob/master/utils.h
CHAR_BIT
而不是8
好奇,因为0xFF00FF00FF00FF00ULL
依赖CHAR_BIT == 8
。注意LL
在常量中不需要。
CHAR_BIT
以增加该宏的显示范围。至于LL,它更是一种注解。这也是我很久以前使用错误的编译器(标准版)捕获的习惯,该编译器无法正确执行操作。
此代码段可以将32位Little Endian数转换为Big Endian数。
#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);
}
如果您在x86或x86_64处理器上运行,则big endian是本机的。所以
对于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);
除非编译器认识到这是字节级操作并生成字节交换代码,否则这不是最有效的解决方案。但这不依赖于任何内存布局技巧,可以很容易地转换为宏。