编辑:第二个程序演示了如何按值传递结构。
这是一个示例,如何为使用 gnu C 标准库的系统定义 printf 自定义转换。程序首先打印 glibc 版本以防 printf API 发生变化(就像过去一样):
#include <stdio.h>
#include <printf.h>
#include <gnu/libc-version.h> // for glibc version information
struct data
{
int d;
char str;
};
int
print_struct_ptr (FILE *stream,
const struct printf_info *info,
const void *const *args)
{
const struct data *aStruct;
int len;
aStruct = *((const struct data **) (args[0]));
/* Simply print the members to the stream */
len = fprintf (stream, "[struct data at %p: d=%d, str=%c]\n",
aStruct, aStruct->d, aStruct->str);
return len;
}
int
print_struct_arginfo_sz (const struct printf_info *info, size_t n,
int *argtypes, int *size)
{
/* We always take exactly one argument and this is a pointer to the
structure.. */
if (n > 0)
argtypes[0] = PA_POINTER;
return 1;
}
int main(void)
{
struct data myStruct = {3, 'A'};
printf("glibc version: %s\n", gnu_get_libc_version());
register_printf_specifier('M', print_struct_ptr, print_struct_arginfo_sz);
return printf("%M\n", &myStruct) > 0 ? 0 : -1;
}
这将打印您的数据结构。 (顺便说一句,将角色成员命名为 str 很奇怪——这应该是一个指针吗?我现在就这样了。)
首先要观察的是我将指针传递给你的结构。这使得注册自定义转换更容易,因为printf 知道如何从变量参数列表中提取指针。通过在print_struct_arginfo_sz() 中插入PA_POINTER 来指示参数是指针。此处不需要其他信息字段。
因为我们传递了一个内置类型,我们只需要定义两个相当简单的回调函数。第一个,print_struct(),确实打印了传递的指针指向的结构,只需通过指针访问其数据成员即可。如前所述,第二个函数通知printf() 要提取的数据类型(指针)。
最后,main() 中的 register_printf_specifier() 调用连接了这些点:它注册了这两个回调(函数指针作为参数传递),将它们与格式说明符 'M' 连接起来。
这是一个示例会话。编译器会警告“未知”转换说明符,并且由于无法识别转换,因此会警告没有转换说明符的参数。这是可以忽略此类警告的极少数情况之一。
$ gcc -Wall -o printf-customization printf-customization.c && ./printf-customization
printf-customization.c: In function ‘main’:
printf-customization.c:43:19: warning: unknown conversion type character ‘M’ in format [-Wformat=]
return printf("%M\n", &myStruct) > 0 ? 0 : -1;
^
printf-customization.c:43:17: warning: too many arguments for format [-Wformat-extra-args]
return printf("%M\n", &myStruct) > 0 ? 0 : -1;
^~~~~~
glibc version: 2.24
[struct data at 0xbf9b6448: d=3, str=A]
为了完整起见,这里是一个结构体按值传递的版本。我在正确解释传递给 print 函数的 args 参数时遇到了困难——它是指向实际参数的指针数组中的第一个元素的指针。显然,args 是一个指向指针的指针,即使对于按值传递的 printf 参数也是如此,也就是说,args[0] 仍然是一个指针,而不是一个简单的结构指针。我不确定为什么会这样。检查printf 来源可能会产生见解,但我真的缺乏时间和动力。这是完整的,又是冗长的程序。
#include <stdio.h>
#include <printf.h>
#include <string.h>
#include <gnu/libc-version.h> // for glibc version information
// We can be gcc specific here and use variadic macros.
#if DEBUG
#define dbg(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
# define dbg(fmt, ...)
#endif
#define err(...) fprintf(stderr, __VA_ARGS__)
/// Our custom type we want to print
struct T { int i; char c; };
/// Callback to extract an struct T from va_list and copy into dest
void getTFromVaList(void *dest, va_list *valist)
{
struct T t = va_arg(*valist, struct T);
// debug: This looks good.
dbg("extracted struct T (%d, %c) to mem %p\n", t.i, t.c, dest);
memcpy(dest, &t, sizeof(t));
}
/// Global variable to communicate conversion type,
/// will be set in main before printf call
static int TArgType;
/// Callback that prints arguments pointed to by pointers
/// in the args array
int printT (FILE *stream,
const struct printf_info *info,
const void *const *args)
{
struct T *tPtr = **(struct T ***)args;
int len;
len = fprintf (stream, "[struct T at %p: t.i=%d, t.c=%c]",
tPtr, tPtr->i, tPtr->c);
if(len <= 0) { err("fprintf to stream failed\n"); }
return len;
}
/// set the argument type connected to specifier T
int fillTArgType( const struct printf_info *info,
size_t n,
int *argtypes,
int *size )
{
// debug: Looks good
dbg("printf_info spec: %c, struct T arg type=%d\n", (char)info->spec, TArgType);
if (n != 1) { err("Weird: n != 1\n"); }
argtypes[0] = TArgType;
*size = sizeof(struct T);
// return the number of arguments required for T, which is 1.
return 1;
}
int main(void)
{
struct T t = { 23, 'A'};
printf("glibc version: %s\n", gnu_get_libc_version());
// Set global variable used in fillTArgType
TArgType = register_printf_type(getTFromVaList);
dbg("T arg type: %d\n", TArgType);
// register specifier
register_printf_specifier('T', printT, fillTArgType);
// try it out
int diag = printf("%T\n", t);
return diag > 0 ? 0 : -1; // 0 bytes printed is failure, too.
}
示例会话:
$ gcc -DDEBUG -Wall -o printf-custom-struct printf-custom-struct.c && ./printf-custom-struct && echo $?
printf-custom-struct.c: In function ‘main’:
printf-custom-struct.c:75:23: warning: unknown conversion type character ‘T’ in format [-Wformat=]
int diag = printf("%T\n", t);
^
printf-custom-struct.c:75:21: warning: too many arguments for format [-Wformat-extra-args]
int diag = printf("%T\n", t);
^~~~~~
glibc version: 2.24
T arg type: 8
printf_info spec: T, struct T arg type=8
extracted struct T (23, A) to mem 0xbfabfdb0
[struct T at 0xbfabfdb0: t.i=23, t.c=A]
0