如何将十六进制字符串解析为指定struct结构体?
首先,咱们先定位你遇到的核心问题:你用strtoull转换字符串后得到的结果不符合预期,大概率是字节序(大小端)不匹配加上printf格式错误导致的误解,再加上最初的位域结构体设计踩了对齐的坑。下面一步步给你解决:
一、先搞清楚为什么你的strtoull转换看起来不对
你代码里注释的printf有个格式错误:
//printf ("data: 0x%%" PRIx64 "\n\n", data);
这里的%%会被解析成单个%,所以实际输出是0x%5abbf13a000a01,而不是正确的十六进制值!这会让你误以为转换后的data值错误。把它改成:
printf("data: 0x%" PRIx64 "\n\n", data);
这样才能正确打印出data的真实值——正常情况下,"5ABBF13A000A01"转成uint64_t应该是0x005ABBF13A000A01(高位补0凑够8字节)。
二、修正字段提取的逻辑(基于strtoull的方案)
你的目标字段对应关系是:
timestamp:字符串前8位5ABBF13A(大端的4字节)refresh_rate:中间4位000A(大端的2字节)flags:最后2位01(1字节)
因为STM32是小端架构,但strtoull解析字符串是按大端逻辑生成数值的,所以正确的字段提取应该是:
// 修正后的字段赋值 sensor.flags = data & 0xFF; // 取最低1字节,正确对应0x01 sensor.rate = (data >> 8) & 0xFFFF; // 取中间2字节,对应0x000A sensor.tstamp = (data >> 24) & 0xFFFFFFFF; // 取最高4字节,对应0x5ABBF13A
你原来的tstamp移位是3 * CHAR_BIT(也就是24位),这个是对的,但前提是data的转换结果正确——先修正printf确认data值,再验证这个逻辑。
三、更可靠的方案:直接分段解析字符串(推荐)
用strtoull把整个字符串转成64位整数再拆分,容易受字节序和补位影响,不如直接按字段分段解析,更直观不易错:
#include <stdio.h> #include <stdint.h> #include <string.h> // 假设你的全局sensor结构体定义如下 typedef struct { uint8_t flags; uint16_t refresh_rate; uint32_t timestamp; } xSensorData2; xSensorData2 sensor; bool bDecode_HexString(char *p) { uint32_t ts_raw; uint16_t rr_raw; uint8_t flags_raw; // 分段解析字符串:前8位是timestamp,中间4位是refresh_rate,最后2位是flags if (sscanf(p, "%8x%4x%2hhx", &ts_raw, &rr_raw, &flags_raw) != 3) { fprintf(stderr, "error: failed to parse hex string.\n"); return false; } // 因为字符串是大端格式,STM32是小端架构,需要转换字节序 sensor.timestamp = __builtin_bswap32(ts_raw); // 32位大端转小端 sensor.refresh_rate = __builtin_bswap16(rr_raw); // 16位大端转小端 sensor.flags = flags_raw; // 单字节无需转换 return true; }
这个方法直接按字段拆分字符串,避免了64位整数的补位和字节序混淆问题,调试起来也更方便。
四、修复你最初的union+结构体方案
你最初用位域失败,是因为位域跨不同类型会触发编译器对齐优化,而且位域的行为在不同编译器里不统一。正确的做法是用强制1字节对齐的结构体+union,再处理字节序:
#pragma pack(push, 1) // 强制1字节对齐,比__attribute__((packed))兼容性更好 typedef struct { uint32_t timestamp; uint16_t refresh_rate; uint8_t flags; } xSensorData2; typedef union { xSensorData2 sensor; uint8_t bytes[7]; // 总字节数:4+2+1=7 } xDataUnion; #pragma pack(pop) // 辅助函数:十六进制字符串转字节数组 int hex_str_to_bytes(const char* hex_str, uint8_t* bytes, int max_bytes) { int len = strlen(hex_str); if (len % 2 != 0 || len/2 > max_bytes) return -1; for (int i = 0; i < len/2; i++) { sscanf(hex_str + 2*i, "%2hhx", &bytes[i]); } return len/2; } // 使用示例 void decode_example() { char teste[50] = "5ABBF13A000A01"; xDataUnion u; if (hex_str_to_bytes(teste, u.bytes, sizeof(u.bytes)) != 7) { fprintf(stderr, "error: invalid hex string length.\n"); return; } // 大端字节数组转小端结构体成员 u.sensor.timestamp = __builtin_bswap32(*(uint32_t*)u.bytes); u.sensor.refresh_rate = __builtin_bswap16(*(uint16_t*)(u.bytes + 4)); u.sensor.flags = u.bytes[6]; // 现在u.sensor就是你需要的结构体了 }
这个方案适合需要直接操作字节流的场景,比如从串口/网络接收数据后直接解析。
最后验证
不管用哪种方案,都要打印结构体成员的值确认:
printf("sensor.flags: 0x%02X\n", sensor.flags); printf("sensor.refresh_rate: 0x%04X\n", sensor.refresh_rate); printf("sensor.timestamp: 0x%08X\n", sensor.timestamp);
正常输出应该是:
sensor.flags: 0x01 sensor.refresh_rate: 0x000A sensor.timestamp: 0x5ABBF13A
内容的提问来源于stack exchange,提问作者R. Rodrigues

