使用带有 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
.
到目前为止我尝试了什么:
%khhx
,不幸的是,这只会导致 k-1
空格后跟两个十六进制字符(一个数据字节)。
%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;
}
目标: 使用单个格式说明符打印可变字节数。
环境: 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
.
到目前为止我尝试了什么:
%khhx
,不幸的是,这只会导致k-1
空格后跟两个十六进制字符(一个数据字节)。%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;
}