C 中 printf() 函数的工作(仅可变数量的参数)
Posted
技术标签:
【中文标题】C 中 printf() 函数的工作(仅可变数量的参数)【英文标题】:Working of printf() function in C (Only Variable number of Arguments) 【发布时间】:2014-07-22 06:06:16 【问题描述】:我问这个问题是为了理解 printf 作为一个接受可变长度参数的函数的工作原理。
我正在从here 学习可变数量的参数概念,让我感到困惑的是传入 va_arg(va_list,datatype) 的数据类型。我的意思是他们在这里提到了一种数据类型。我们需要传递具有不同数据类型的参数的情况呢? printf 函数也是如此。
printf 如何准确地计算出不同类型的参数类型。根据我的想法,他们必须检查第一个 const char* 参数中的每个 % 符号,然后检查特定数据类型的标记。
【问题讨论】:
快速链接帮助***.com/questions/2457656/… @Jayesh 感谢您的链接,但我不是在寻找硬件级别。只有 va_arg() 中的数据类型标识。我的意思是他们如何在多个不同的数据类型参数中实际识别特定的数据类型 是的,它会检查%X
以查看要使用的数据类型或标志。没有这个,它就无法知道。唯一的方法是使用 C++ 的可变参数模板。
printf
仅从格式说明符中知道类型。相关:Why do I have to specify data type each time in C?.
顺便说一句,如果您使用 C++ 编写,请使用流而不是 C 中的旧函数
【参考方案1】:
以下变量参数列表函数解释了printf
的工作原理。
#include <stdio.h>
#include <stdarg.h>
main()
// call the foo function here
// like foo("%d%s%c",3,"hai",'a');
void foo(char *fmt, ...) // This Function works like same as printf
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
switch (*fmt++)
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* need a cast here since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
va_end(ap);
更多参考请参见va_arg的手册页
它不支持char
和float
值,所以我们需要对它进行类型转换。
对于float
,您需要键入转换double
值。
【讨论】:
最好能把va_arg
的man page做成一个链接。
那将是 foo("dsc" 而不是 foo("%d%s%c"
另外,要么函数声明的顺序必须颠倒,要么你需要上面的函数原型。【参考方案2】:
printf
是一个函数,而不是宏。它被定义为
int printf(const char *, ...)
并且有可变数量的参数。
printf
使用字符串来定义传递的参数数量。每个 % 用于向堆栈移动并检索参数。
所以,如果你通过了
"%d %d %d", 1, 2
然后将显示 1、2 和任意值。这很糟糕:你可以使用这个函数遍历堆栈。
通过时
"%d %d", 1, 2, 3
然后会显示 1 和 2。并且行为未定义:通常使用 __cdecl 调用约定,因此堆栈不会因为被调用者清理而损坏。
【讨论】:
如果想检查什么,请参考cplusplus.com。他们通常对所有库都有解释。 @anujpradhan “我认为 printf 是从 _printf 派生的”——不知道你的意思是什么,但是……不。 printf 从 C 的早期就已经存在,并且不是从其他函数派生的。【参考方案3】:是的,它会检查 %X 以查看要使用的数据类型或标志。没有这个,它就无法知道。唯一的方法是使用 C++ 的可变参数模板。除此之外,C 如下所示..
此示例使用fwrite
将数据写入stdout
。
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstdarg>
void C_Printf(const char *fmt, ...)
int fmtLength = strlen(fmt);
va_list VariableArgs;
va_start(VariableArgs, fmt);
for (int I = 0; I < fmtLength; I++)
if (fmt[I] == '%')
switch(tolower(fmt[++I]))
case 'f':
double d = va_arg(VariableArgs, double);
fwrite(&d, sizeof(double), 1, stdout);
break;
case 'i':
case 'd':
int i = va_arg(VariableArgs, int);
fwrite(&i, sizeof(int), 1, stdout);
break;
case 's':
const char *str = va_arg(VariableArgs, const char *);
fwrite(&str[0], sizeof(char), strlen(str), stdout);
break;
default:
break;
else
fwrite(&fmt[I], sizeof(char), 1, stdout);
va_end(VariableArgs);
int main()
C_Printf("%s", "hello there");
【讨论】:
【参考方案4】:在某些free software 标准C 库实现中查看printf
的源代码,例如GNU libc 或 musl-libc。我发现musl-libc
非常易读,请查看src/stdio/printf.c 内部,然后查看src/stdio/vfprintf.c(实际工作完成的地方)。当然,它根据格式控制字符串使用va_arg
(请参阅stdarg(3)。请注意,va_arg
是在编译器内部通过__builtin_va_arg
在GCC 中实现的)。 GCC 还内置了对printf
的支持
【讨论】:
以上是关于C 中 printf() 函数的工作(仅可变数量的参数)的主要内容,如果未能解决你的问题,请参考以下文章