使用带有 printf 的格式字符串打印可变字节数

Posted

技术标签:

【中文标题】使用带有 printf 的格式字符串打印可变字节数【英文标题】:Printing variable number of bytes using format strings with printf 【发布时间】:2022-01-05 02:41:36 【问题描述】:

目标:使用单个格式说明符打印可变数量的字节。

环境: x86-64 Ubuntu 20.04.3 LTS 在 x86-64 主机上的 VM 中运行。

示例:

%kmagic 成为我正在寻找的格式说明符,它通过从堆栈中弹出它们并将它们添加到输出中来打印k 字节。然后,对于指向内存中保存字节0xde 0xad 0xbe 0xef 的内存区域的%rsp,我希望printf("Next 4 bytes on the stack: %4magic") 打印Next 4 bytes on the stack: deadbeef

到目前为止我尝试了什么:

    %khhx,不幸的是,这只会导致 k-1 空格后跟两个十六进制字符(一个字节的数据)。 %kx,我希望将 k/2 字节打印为一个数字。这只会打印 8 个十六进制字符(4 个字节),前面有 k - 8 个空格。

打印的非空白字符数与格式说明符的长度匹配,即%hhx 的预期长度为 2,这也是打印的非空白字符数。 %x 也是如此,预计会打印 8 个字符。

问题: 是否有可能获得所需的行为?如果有,怎么做?

【问题讨论】:

"是否可以获得所需的行为?" --> 是的。 “如果是,怎么做?”你在找人为你写这段代码吗? “到目前为止我尝试了什么:” --> 发布该代码。 作为问题中的状态,我正在寻找一个格式说明符,而不是一个自定义的 printf 实现,它可以满足我的需求。所以不,我不是在找人为我编写代码,而是在找人告诉我说明符。当我正在寻找说明符而不是任何代码时,编写我尝试过的说明符就足够了。发布“代码”,例如printf("I want 4 bytes: %4hhx"); 只是添加样板。 【参考方案1】:

是否有可能获得所需的行为?如果有,怎么做?

不存在 printf 格式说明符来做你想做的事。

可能

编写您自己的printf 实现来支持您想要的。使用 implementation-specific tools 创建您自己的 printf 格式说明符。您可以从linux kernel printk %*phN format speciifer 获得灵感。

【讨论】:

【参考方案2】:

无法使用标准printf。需要自己编写函数,自定义printf函数。

http://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html

示例(简单转储):

int printdump (FILE *stream, const struct printf_info *info, const void *const *args)

    const unsigned char *ptr = *(const unsigned char **)args[0];
    size_t size = *(size_t*)args[1];
    for(size_t i = 1; i <= size; i++)
    
        fprintf(stream, "%02X%c", ptr[i-1], i % 8 ? ' ' : '\n');
    
  return 1;


int printdumpargs (const struct printf_info *info, size_t n, int *argtypes)

  if (n == 2)
    argtypes[0] = PA_POINTER;
    argtypes[1] = PA_INT;
  return 2;


int main(void)

    double x[4] = 456543645.6786e45, 456543654, 1e345, -345.56e67;
    register_printf_function ('Y', printdump, printdumpargs);

    printf("%Y\n", &x, sizeof(x));

我看到它现在已经贬值了(可能没有人使用它)

https://godbolt.org/z/qKs6e1d9q

输出:

30 18 CB 5A EF 10 13 4B
00 00 00 A6 4D 36 BB 41
00 00 00 00 00 00 F0 7F
C4 5D ED 48 9C 05 60 CE

【讨论】:

printdumpargs 似乎有缩进问题,或者可能是缺少块。【参考方案3】:

没有用于您的目的的标准转换说明符,但您可以使用辅助函数和动态数组在 C99 中实现您的目标:

#include <stdio.h>

char *dump_bytes(char *buf, const void *p, size_t count) 
    const unsigned char *src = p;
    char *dest = buf;
    while (count --> 0) 
        dest += sprintf(dest, "%.2X", *src++);
        if (count)
            *dest++ = ' ';
    
    *dest = '\0';  // return an empty sting for an empty memory chunk
    return buf;


int main() 
    long n = 0x12345;
    printf("n is at address %p with contents: %s\n",
           (void *)&n,
           dump_bytes((char[3 * sizeof(n)])"", &n, sizeof(n)));
    return 0;

输出:n is at address 0x7fff523f57d8 with contents: 45 23 01 00 00 00 00 00

您可以使用宏进行更简单的调用:

#define DUMPBYTES(p, n)  dump_bytes((char[3 * (n)])"", p, n)

int main() 
    char *p = malloc(5);
    printf("allocated 5 bytes at address %p with contents: %s\n",
           p, DUMPBYTES(p, 5));
    free(p);
    return 0;

【讨论】:

以上是关于使用带有 printf 的格式字符串打印可变字节数的主要内容,如果未能解决你的问题,请参考以下文章

解压缩具有可变长度的未知序列化格式

JAVA格式化打印printf的使用

将带有参数的 printf 用于可变参数函数?

使用 printf 打印 UTF-8 字符串 - 宽与多字节字符串文字

printf在C语言中啥意思

STM32中使用printf打印字符串为何字符串第一个字符无法打印?