如何通过printf格式字符串用单个说明符打印可变数量字节?
答案
结论
标准C库的printf没有原生支持该需求的单个格式说明符,但结合你使用的Ubuntu 20.04自带的GNU libc环境,可以通过两种方案实现预期效果。
方案1:使用GNU libc自定义格式说明符(实现你提到的%kmagic效果)
GNU libc提供了register_printf_function接口,允许你自定义格式说明符的处理逻辑:
- 注册自定义说明符比如
%M,约定前缀的数字参数为要读取的字节数k - 处理函数中直接从可变参数栈读取k个连续字节,逐字节转换为两位十六进制字符输出即可
示例代码片段:
#include <printf.h> #include <stdint.h> static int print_bytes(FILE *stream, const struct printf_info *info, const void *const *args) { int k = info->width; // 取格式说明符前缀的数字作为字节数 const uint8_t *buf = *(const uint8_t **) args[0]; // 取栈指针参数 for (int i = 0; i < k; i++) { fprintf(stream, "%02hhx", buf[i]); } return k * 2; } static int print_bytes_arginfo(const struct printf_info *info, size_t n, int *argtypes, int *size) { if (n > 0) argtypes[0] = PA_POINTER; return 1; } // 注册后就可以用%<k>M格式说明符,参数传栈地址即可输出k字节十六进制 int main() { register_printf_function('M', print_bytes, print_bytes_arginfo); uint32_t test = 0xefbeadde; // 小端存储对应0xde 0xad 0xbe 0xef printf("Next 4 bytes on the stack: %4M\n", &test); // 输出:Next 4 bytes on the stack: deadbeef return 0; }
方案2:无自定义扩展的替代方案
如果不想修改libc的printf行为,可直接重复使用%.2hhx说明符,每个说明符对应读取1字节:
- 要输出4字节就写
printf("Next 4 bytes on the stack: %.2hhx%.2hhx%.2hhx%.2hhx\n", *(uint8_t*)rsp, *(uint8_t*)(rsp+1), *(uint8_t*)(rsp+2), *(uint8_t*)(rsp+3)); - 输出结果和预期完全一致,唯一缺点是需要对应写k个格式说明符,不是单个说明符。
为什么你之前的尝试不生效
你用到的%khhx、%kx中的k是宽度修饰符,作用是指定输出的最小总长度,不足时补空格,和读取多少字节无关:
%hhx固定只读取1个字节,输出2个十六进制字符,你写%4hhx就会输出2个字符加2个前置空格,总长度为4,和你观察到的现象一致%x在x86-64环境下固定读取4字节unsigned int类型,输出最多8个十六进制字符,你写%10x就会输出8个字符加2个前置空格,总长度为10。
内容的提问来源于stack exchange,提问作者iMrFelix
相关产品推荐
相关产品推荐

