使用标准库函数C标准库提供了几个函数来处理字节序转换,这些函数通常用于网络编程。它们包括 htons、ntohs、htonl 和 ntohl,分别用于16位和32位整数的转换。对于64位整数,虽然标准库没有直接提供 htonll 和 ntohll 函数,但可以使用 htonl 和 ntohl 来组合实现。
32位整数的转换
#include <stdio.h>
#include <arpa/inet.h> // 包含字节序转换函数
uint32_t htonl_32(uint32_t hostlong) {
return htonl(hostlong);
}
uint32_t ntohl_32(uint32_t netlong) {
return ntohl(netlong);
}
64位整数的转换
#include <stdint.h>
uint64_t htonll(uint64_t value) {
union {
uint64_t ll;
uint8_t b[8];
} n, h;
n.ll = value;
h.b[0] = n.b[7];
h.b[1] = n.b[6];
h.b[2] = n.b[5];
h.b[3] = n.b[4];
h.b[4] = n.b[3];
h.b[5] = n.b[2];
h.b[6] = n.b[1];
h.b[7] = n.b[0];
return h.ll;
}
uint64_t ntohll(uint64_t value) {
return htonll(value);
}
手动实现字节序转换
如果你不想依赖于标准库函数,或者需要更通用的解决方案,可以手动实现字节序转换。
32位整数的手动转换
#include <stdint.h>
uint32_t htonl_manual(uint32_t x) {
return (x >> 24) | ((x & 0x00FF0000) >> 8) | ((x & 0x0000FF00) << 8) | (x << 24);
}
uint32_t ntohl_manual(uint32_t x) {
return htonl_manual(x); // 大端到小端和小端到大端的转换是相同的
}
64位整数的手动转换
#include <stdint.h>
uint64_t htonll_manual(uint64_t x) {
return ((x & 0xFF00000000000000ULL) >> 56) |
((x & 0x00FF000000000000ULL) >> 40) |
((x & 0x0000FF0000000000ULL) >> 24) |
((x & 0x000000FF00000000ULL) >> 8) |
((x & 0x00000000FF000000ULL) << 8) |
((x & 0x0000000000FF0000ULL) << 24) |
((x & 0x000000000000FF00ULL) << 40) |
((x & 0x00000000000000FFULL) << 56);
}
uint64_t ntohll_manual(uint64_t x) {
return htonll_manual(x); // 大端到小端和小端到大端的转换是相同的
}
示例代码下面是一个完整的示例代码,展示了如何使用上述函数进行32位和64位整数的大端和小端之间的转换。
#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h> // 包含字节序转换函数
// 32位整数的转换
uint32_t htonl_32(uint32_t hostlong) {
return htonl(hostlong);
}
uint32_t ntohl_32(uint32_t netlong) {
return ntohl(netlong);
}
// 64位整数的转换
uint64_t htonll(uint64_t value) {
union {
uint64_t ll;
uint8_t b[8];
} n, h;
n.ll = value;
h.b[0] = n.b[7];
h.b[1] = n.b[6];
h.b[2] = n.b[5];
h.b[3] = n.b[4];
h.b[4] = n.b[3];
h.b[5] = n.b[2];
h.b[6] = n.b[1];
h.b[7] = n.b[0];
return h.ll;
}
uint64_t ntohll(uint64_t value) {
return htonll(value);
}
int main() {
uint32_t little_endian_32 = 0x12345678;
uint64_t little_endian_64 = 0x123456789ABCDEF0;
printf("Little Endian 32-bit: 0x%08X\n", little_endian_32);
printf("Big Endian 32-bit: 0x%08X\n", htonl_32(little_endian_32));
printf("Back to Little Endian 32-bit: 0x%08X\n", ntohl_32(htonl_32(little_endian_32)));
printf("\n");
printf("Little Endian 64-bit: 0x%016lX\n", (unsigned long long)little_endian_64);
printf("Big Endian 64-bit: 0x%016lX\n", (unsigned long long)htonll(little_endian_64));
printf("Back to Little Endian 64-bit: 0x%016lX\n", (unsigned long long)ntohll(htonll(little_endian_64)));
return 0;
}
输出
Little Endian 32-bit: 0x12345678
Big Endian 32-bit: 0x78563412
Back to Little Endian 32-bit: 0x12345678
Little Endian 64-bit: 0x123456789abcdef0
Big Endian 64-bit: 0xf0debc9a78563412
Back to Little Endian 64-bit: 0x123456789abcdef0
## 总结
32位整数:使用 htons、ntohs、htonl 和 ntohl 函数。
64位整数:可以使用联合体或手动位操作来实现 htonll 和 ntohll 函数。
手动实现:通过位移和按位与操作来实现字节序转换。
通过这些方法,你可以轻松地在C语言中进行32位和64位整数的大端和小端之间的转换。
附录 位操作
#include <stdio.h>
#include <stdlib.h>
///防止头文件重复包含
#ifdef _HEAD_H_
#define _HEAD_H_
///如果C++ 引用,提醒C++编译器用C语言的方式去编译
#ifdef __cplusplus
extern "C"{
#endif
///对字节操作
#define GET_LOW_BYTE0(x) ((x >> 0) & 0x000000ff) /* 获取第0个字节 */
#define GET_LOW_BYTE1(x) ((x >> 8) & 0x000000ff) /* 获取第1个字节 */
#define GET_LOW_BYTE2(x) ((x >> 16) & 0x000000ff) /* 获取第2个字节 */
#define GET_LOW_BYTE3(x) ((x >> 24) & 0x000000ff) /* 获取第3个字节 */
#define CLEAR_LOW_BYTE0(x) (x &= 0xffffff00) /* 清零第0个字节 */
#define CLEAR_LOW_BYTE1(x) (x &= 0xffff00ff) /* 清零第1个字节 */
#define CLEAR_LOW_BYTE2(x) (x &= 0xff00ffff) /* 清零第2个字节 */
#define CLEAR_LOW_BYTE3(x) (x &= 0x00ffffff) /* 清零第3个字节 */
#define SET_LOW_BYTE0(x) (x |= 0x000000ff) /* 第0个字节置1 */
#define SET_LOW_BYTE1(x) (x |= 0x0000ff00) /* 第1个字节置1 */
#define SET_LOW_BYTE2(x) (x |= 0x00ff0000) /* 第2个字节置1 */
#define SET_LOW_BYTE3(x) (x |= 0xff000000) /* 第3个字节置1 */
///对位节操作
#define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) /* 获取第bit位 */
#define CLEAR_BIT(x, bit) (x &= ~(1 << bit)) /* 清零第bit位 */
#define SET_BIT(x, bit) (x |= (1 << bit)) /* 置位第bit位 */
#define BIT_M_TO_N(x, m, n) ((unsigned int)(x << (31-(n))) >> ((31 - (n)) + (m))) /* 获取第[n:m]位的值 eg 第二位到第三位:[2:3] */
///大小端转换
// 4bytes、32bit数据大小端转化
#define L2B32(Little) (((Little & 0xff) << 24) | (((Little) & 0xff00) << 8) | (((Little) & 0xff0000) >> 8) | ((Little >> 24) & 0xff))
// 2bytes、16bit数据大小端转化
#define L2B16(Little) (((Little & 0xff) << 8) | ((Little >> 8) & 0xff))
/// u0_Printf/u1_Printf/u2_Printf 三个串口的打印函数,可以自由切打印函数
void (*Printf)(char* fmt,...);
///选择串口
#define UART 2
///选择打印内容
#define DEBUGALL 0
#define DEBUGTIME 0
#define NOTHING 0
#if UART == 0
#define Printf u0_Printf
#elif UART == 1
#define Printf u1_Printf
#else
#define Printf u2_Printf
#endif
#if DEBUGALL
#define DBG_PRINTF(fmt, args...) \
{\
Printf("<<File:%s Line:%d Function:%s>> ", __FILE__, __LINE__, __FUNCTION__);\
Printf(fmt, ##args);\
}
#elif DEBUGTIME
#define DBG_PRINTF(fmt, args...) \
{\
Printf("<<Time:%s>> ", __TIME__);\
Printf(fmt, ##args);\
}
#elif NOTHING
#define DBG_PRINTF(fmt, args...) \
{\
NOP();\
}
#else
#define DBG_PRINTF(fmt, args...)\
{\
Printf(fmt, ##args);\
}
#endif
///函数外声明
extern void DeBug_Init(void);
extern void DeBug_Task(void);
#ifdef __cplusplus
};
#endif
#endif _HEAD_H_