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

Printing variable number of bytes using format strings with printf

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

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

示例:

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

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

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

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

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

Is it possible to get the desired behavior? If so, how?

不存在 printf 格式说明符来执行您想要的操作。

Is it possible

编写您自己的 printf 支持您想要的实现。使用 implementation-specific tools to create your own printf format specifier. You can take inspiration from linux kernel printk %*phN format speciifer.

无法使用标准 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

没有适合您目的的标准转换说明符,但您可以使用辅助函数和动态数组在 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 = '[=10=]';  // 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;
}