
做物联网项目时STM32给服务器发了一个温度值服务器收到的却是乱码十有八九是大小端没处理。这篇讲透大小端原理、怎么判断、怎么转换。一、什么是大端和小端1.1 定义一个32位整数0x12345678在内存里怎么存大端Big Endian高字节存低地址 地址: 0x00 0x01 0x02 0x03 数据: 12 34 56 78 小端Little Endian低字节存低地址 地址: 0x00 0x01 0x02 0x03 数据: 78 56 34 12记忆大端 大字节在前低地址小端 小字节在前低地址。1.2 谁用大端谁用小端平台字节序ARM Cortex-M (STM32)小端x86/x64 (PC)小端网络协议 (TCP/IP)大端网络字节序为什么网络用大端这是历史原因——早期互联网1980年代的主机各种字节序都有TCP/IP设计者规定统一用大端作为网络字节序大家发数据前先转成大端收到后再转回自己的字节序。 htonl/htons函数就是干这个的host to network long/short。| 部分PowerPC/MIPS | 大端或可配置 |关键STM32是小端网络是大端。两者通信必须转换二、怎么判断当前机器是大端还是小端2.1 代码判断intcheck_endian(void){uint32_tx0x12345678;uint8_t*p(uint8_t*)x;if(*p0x78){return1;/* 小端低字节(0x78)在低地址 */}else{return0;/* 大端高字节(0x12)在低地址 */}}2.2 利用union判断union{uint16_tval;uint8_tbytes[2];}endian_test;endian_test.val0x0102;if(endian_test.bytes[0]0x02){/* 小端 */}else{/* 大端 */}三、字节序转换函数3.1 标准库函数Linux/Windows上用注意这些函数在Linux(arpa/inet.h)和Windows(winsock2.h)上有但STM32上没有这些头文件需要自己实现见3.2节。如果你在电脑上写网络程序可以直接用这些函数。#includearpa/inet.h/* Linux *//* 或 Windows: #include winsock2.h */htonl(uint32_thostlong);/* host to network long (32位) */htons(uint16_thostshort);/* host to network short (16位) */ntohl(uint32_tnetlong);/* network to host long */ntohs(uint16_tnetshort);/* network to host short */h host主机字节序n network网络字节序l long(32位)s short(16位)。3.2 STM32上没有这些头文件怎么办自己实现/* 16位字节序交换 */uint16_tswap16(uint16_tval){return(val8)|(val8);}/* 32位字节序交换 */uint32_tswap32(uint32_tval){return((val24)0x000000FF)|((val8)0x0000FF00)|((val8)0x00FF0000)|((val24)0xFF000000);}/* 如果是小端机器(STM32)发送前转大端 */#definehtons(x)swap16(x)#definehtonl(x)swap32(x)#definentohs(x)swap16(x)#definentohl(x)swap32(x)ARM有专用指令__REV()(32位反转)和__REV16()(16位反转)比C实现快。四、实战STM32和服务器通信4.1 问题场景STM32采集温度25.5°C用float表示 0x41CC0000IEEE754标准。IEEE754是什么IEEE754是浮点数在内存中的表示标准。float是32位其中1位符号8位指数23位尾数。25.5按这个标准编码就是0x41CC0000。这里不需要深入理解编码原理只需要知道float也是4字节、也有字节序问题。直接发送4字节给服务器服务器大端解析成了0x0000CC41 一个极小的数。4.2 解决方案/* 发送前转网络字节序 */floattemperature25.5f;uint32_ttemp_raw;memcpy(temp_raw,temperature,4);/* float转uint32 */temp_rawhtonl(temp_raw);/* 转大端发送 *//* 服务器端大端直接解析即可 */4.3 协议帧设计建议最佳实践协议里明确规定字节序通常用大端网络字节序。#pragmapack(push,1)typedefstruct{uint8_theader;/* 0xAA1字节无字节序问题 */uint16_tlength;/* 大端存储 */uint32_ttimestamp;/* 大端存储 */uint8_tdata[32];uint8_tchecksum;}frame_t;#pragmapack(pop)/* 发送前转换 */frame.lengthhtons(length);frame.timestamphtonl(timestamp);五、字符串和字节数组没有字节序问题/* 字符串和byte数组没有字节序问题 *//* 因为它们是逐字节的不涉及多字节整数 */uint8_tmac[6]{0x01,0x02,0x03,0x04,0x05,0x06};/* 不管大端小端内存里都是 01 02 03 04 05 06 */char*strhello;/* 不管大端小端内存里都是 h e l l o */规则只有多字节整数uint16_t/uint32_t/float才需要考虑字节序。单字节数据uint8_t/char数组不需要。六、常见踩坑坑1float直接发接收端解析错误float的4字节也有字节序问题。发送前htonl转换或用JSON/字符串传输避免二进制。坑2union在不同平台行为不同union里存uint32_t再按byte取结果依赖平台字节序。跨平台代码避免这种用法。坑3以为所有ARM都是小端大部分Cortex-M是小端但Cortex-M3/M4/M7支持可配置字节序虽然默认小端。PowerPC和部分DSP是大端。通信时别假设对方和你一样。七、总结要点内容大端高字节存低地址网络字节序小端低字节存低地址STM32/x86判断方法用union或指针取首字节判断转换函数htonl/htons/ntohl/ntohs无字节序问题uint8_t数组、字符串一句话总结STM32(小端)和服务器(大端)通信时多字节整数必须用htonl/htons转换字符串和字节数组不用管。下一篇预告《C语言宏的陷阱那些容易踩坑的#define》如果这篇文章对你有帮助点赞 收藏 关注这是我持续更新的动力有问题欢迎评论区交流我会逐条回复。作者嵌入式阿蔡