处理INT_MIN、LONG_MIN时void*转换问题及自定义printf故障排查
自定义printf的%p格式处理修复方案
我在实现自定义printf的%p格式处理函数时,要求使用write和while循环(禁止使用for和fputc)。当前代码在常规场景下可用,但处理INT_MIN、LONG_MIN、INT_MAX这类值时输出异常,无法匹配标准printf的结果。
原代码:
static int write_hex(unsigned char *buffer, int i) { char tmp; int leading_zero; int counter; leading_zero = 1; counter = 0; tmp = "0123456789abcdef"[buffer[i] / 16]; if (tmp != '0' || !leading_zero) { write(1, &tmp, 1); leading_zero = 0; counter++; } tmp = "0123456789abcdef"[buffer[i] % 16]; if (tmp != '0' || !leading_zero) { write(1, &tmp, 1); leading_zero = 0; counter++; } return (counter); } int handle_pointer(va_list args) { int i; int counter; void *ptr; unsigned char *buffer; ptr = va_arg(args, void*); buffer = malloc(sizeof(ptr)); ft_memcpy(buffer, &ptr, sizeof(ptr)); i = 0; write(1, "0x", 2); counter = 2; while (buffer[i]) { counter += write_hex(buffer, i); i++; } free(buffer); return (counter); }
测试失败结果:
args: [" %p %p ", INT_MIN, INT_MAX] printf: [ 0x80000000 0x7fffffff ] = 23 ft_printf: [ 0x 0xffffff7f ] = 15
问题分析
- 循环条件错误:原代码用
while(buffer[i])判断是否继续遍历,会跳过所有值为0的字节。而指针的二进制表示中可能包含多个0字节(比如INT_MIN在小端系统中的内存有三个0字节),导致部分字节未被输出。 - 字节序颠倒:标准
printf输出指针的十六进制值时,按高位到低位的顺序输出;而原代码直接按内存中的字节序(小端系统是低字节在前)输出,导致顺序完全颠倒。 - 前导零逻辑错误:
write_hex函数对每个字节单独处理前导零,导致跨字节的前导零处理混乱,同时无法正确识别全局的前导零(比如指针值的高位全零但低位有值的情况)。
修复后的代码
static int write_hex_char(unsigned char c) { char tmp; int counter = 0; // 输出高4位 tmp = "0123456789abcdef"[c / 16]; write(1, &tmp, 1); counter++; // 输出低4位 tmp = "0123456789abcdef"[c % 16]; write(1, &tmp, 1); counter++; return counter; } int handle_pointer(va_list args) { int i; int counter; void *ptr; unsigned char *buffer; unsigned char *start; size_t ptr_size; ptr = va_arg(args, void*); ptr_size = sizeof(ptr); buffer = malloc(ptr_size); if (!buffer) return 0; ft_memcpy(buffer, &ptr, ptr_size); write(1, "0x", 2); counter = 2; // 找到第一个非零字节(从高位到低位) start = buffer + ptr_size - 1; while (start >= buffer && *start == 0) start--; // 如果全零(NULL指针),输出一个0 if (start < buffer) { write(1, "0", 1); counter++; } else { // 从第一个非零字节开始,按高位到低位输出每个字节的十六进制 i = start - buffer; while (i >= 0) { counter += write_hex_char(buffer[i]); i--; } } free(buffer); return counter; }
修复说明
- 调整循环逻辑:先计算指针的字节长度
ptr_size,遍历所有字节不再依赖字节值是否为0。 - 修正字节序:从指针内存的最高位字节开始遍历,找到第一个非零字节后,按高位到低位的顺序输出,匹配标准
printf的输出顺序。 - 全局前导零处理:先跳过所有高位的零字节,只输出从第一个非零字节开始的内容;如果指针是
NULL(全零),则输出0x0。 - 简化write_hex函数:改为
write_hex_char,直接输出单个字节的两位十六进制,不再处理局部前导零,全局前导零统一在handle_pointer中处理。
内容的提问来源于stack exchange,提问作者Vinicius Bass
相关产品推荐
相关产品推荐

