从int变量提取字节时高位字节符号扩展异常问题排查
问题描述
尝试从一个int变量中提取4个char值,但所有尝试均得到相同结果:高低字的低字节读取正确,但高字节被扩展为int时填充了全1。以下是使用GCC编译的测试代码:
#include <stdio.h> #define PRINT printf("b1 %u\n", bb1);\ printf("b2 %u\n", bb2);\ printf("b3 %u\n", bb3);\ printf("b4 %u\n", bb4) typedef union{ int integer; struct{ char b1; char b2; char b3; char b4; }; }bytes; typedef struct{ char b1:8; char b2:8; char b3:8; char b4:8; }bitfield; int main() { int a; printf("int size = %u\n", a=sizeof(int)); printf("char size = %u\n", a=sizeof(char)); bytes in_union; bitfield in_struct; // values stored in bytes // hword.hi 240 hword.lo 15 // lword.hi 129 lword.lo 126 int value = 4027548030; // #1 solution with union in_union.integer = value; char bb1 = in_union.b1; char bb2 = in_union.b2; char bb3 = in_union.b3; char bb4 = in_union.b4; PRINT; // #2 with pointer to union char* uptr; uptr = (char*)&in_union; bb1 = *uptr; bb2 = *(uptr+1); bb3 = *(uptr+2); bb4 = *(uptr+3); PRINT; // #3 with void pointer to union void* vptr; vptr = &in_union; bb1 = *(char*)vptr; bb2 = *(char*)(vptr+1); bb3 = *(char*)(vptr+2); bb4 = *(char*)(vptr+3); PRINT; // #4 with pointer to value variable char* ptr = (char*)&value; bb1 = *ptr; bb2 = *(ptr+1); bb3 = *(ptr+2); bb4 = *(ptr+3); PRINT; // #5 with bitfield in_struct.b1 = (char)value; in_struct.b2 = (char)(value>>8); in_struct.b3 = (char)(value>>16); in_struct.b4 = (char)(value>>24); bb1 = in_struct.b1; bb2 = in_struct.b2; bb3 = in_struct.b3; bb4 = in_struct.b4; PRINT; return 0; }
实际输出:
b1 126 b2 4294967169 b3 15 b4 4294967280
预期输出:
b1 126 b2 129 b3 15 b4 240
问题原因与解决方法
核心原因
问题出在**char类型的符号扩展**:
- 多数编译器(包括GCC)中,
char默认是有符号类型(signed char)。 - 当提取的字节值大于127(比如129、240)时,这些值在有符号char中会被解析为负数(129对应-127,240对应-16)。
- 把这些负数的
char传给printf的%u(无符号整数)格式符时,会先进行整数提升:负数以符号扩展方式填充高位为全1,最终转换为超大的无符号整数,这就是你看到的异常结果。
解决方法
有两种直接有效的修复方式:
方法1:使用unsigned char替代char
将代码中所有char类型替换为unsigned char,包括结构体、联合体成员及临时变量:
typedef union{ int integer; struct{ unsigned char b1; unsigned char b2; unsigned char b3; unsigned char b4; }; }bytes; typedef struct{ unsigned char b1:8; unsigned char b2:8; unsigned char b3:8; unsigned char b4:8; }bitfield; // 临时变量同步修改 unsigned char bb1, bb2, bb3, bb4;
unsigned char会把所有字节值当作无符号数处理,不会产生符号扩展,传递给%u时能正确输出原始字节值。
方法2:输出时强制转换为unsigned char
如果不想修改类型定义,可在printf中把char强制转换为unsigned char,再转为unsigned int匹配格式:
修改PRINT宏:
#define PRINT printf("b1 %u\n", (unsigned int)(unsigned char)bb1);\ printf("b2 %u\n", (unsigned int)(unsigned char)bb2);\ printf("b3 %u\n", (unsigned int)(unsigned char)bb3);\ printf("b4 %u\n", (unsigned int)(unsigned char)bb4)
先转换为unsigned char消除符号扩展,再转为unsigned int适配%u的格式要求。
注:你的预期输出中bb2应为129而非127,属于注释对应值的笔误。
内容的提问来源于stack exchange,提问作者Tomasz Nowak
相关产品推荐
相关产品推荐

