带有 `fcn( char *, ...)` 的可变参数函数它怎么知道什么时候结束?
variadic functions with `fcn( char *, ...)` how does it know when to end?
我一直在审查来自多个来源的内容,包括我最喜欢的 K&R 第二版。
我一直在研究可变参数函数,我看到的几乎所有教程都在省略号之前使用 and int
来确定传递的变量参数总数。
但是书中的K&R例子没有用int
,他们用的是char *
我搜索了 Whosebug,发现了类似的东西:
Any variadic function must have a way for the caller to specify the
number and types of arguments. The *printf functions, for example, do
so via the (non-variadic) format string.
和
No -- C doesn't define end as having any special meaning with varargs.
When you write a function that takes a variable argument list, it's up
to you to decide how to tell it how long of a list has been passed.
link
K&R minprintf 旨在展示如何使用可变函数。
---- to show how to write a function that processes a variable-length
argument list in a portable way. Since we are mainly interested in the
argument processing, minprintf will process the format string and
arguments but will call the real printf to do the format conversions
K&R只展示了功能,我在最下面加了main,看看能不能用。我用 gcc -Wall test.c
编译了它。它在没有警告的情况下编译,并且按预期工作。代码是:
#include <stdarg.h>
#include <stdio.h>
/* minprintf: minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
va_list ap; /* points to each unnamed arg in turn */
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt); /* make ap point to 1st unnamed arg */
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival = va_arg(ap, int);
printf("%d", ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap); /* clean up when done */
}
// I added this main below to test K&R minprintf function. It compiled no errors and gave right answer.
int main()
{
int i = 25;
int j = 21;
char str[] = "This is a test";
minprintf("%d, %d, %s\n", i, j, str);
}
minprintf
怎么知道什么时候结束?里面有NULL
吗? K&R 不解释。从我在网上阅读的内容和上面的一些引述来看,可变参数函数不知道在哪里结束,除非你告诉它,比如在省略号前加上一个 int。
是的,里面有一个[=11=]
。查看循环的终止条件:
for (p = fmt; *p; p++) { // look here
// ...
}
当p
不指向[=11=]
值时,*p != '[=14=]'
,即(bool)*p
是true
。在这种情况下,*p
被处理,无论是 %
还是其他。
当p
指向[=11=]
值时,(bool)*p
是false
。循环结束,va_end
被调用。
因此,在扫描完字符串、处理所有 %
说明符后,它会通过检查 字符串结尾 .
来终止
我一直在审查来自多个来源的内容,包括我最喜欢的 K&R 第二版。
我一直在研究可变参数函数,我看到的几乎所有教程都在省略号之前使用 and int
来确定传递的变量参数总数。
但是书中的K&R例子没有用int
,他们用的是char *
我搜索了 Whosebug,发现了类似的东西:
Any variadic function must have a way for the caller to specify the number and types of arguments. The *printf functions, for example, do so via the (non-variadic) format string.
和
No -- C doesn't define end as having any special meaning with varargs. When you write a function that takes a variable argument list, it's up to you to decide how to tell it how long of a list has been passed. link
K&R minprintf 旨在展示如何使用可变函数。
---- to show how to write a function that processes a variable-length argument list in a portable way. Since we are mainly interested in the argument processing, minprintf will process the format string and arguments but will call the real printf to do the format conversions
K&R只展示了功能,我在最下面加了main,看看能不能用。我用 gcc -Wall test.c
编译了它。它在没有警告的情况下编译,并且按预期工作。代码是:
#include <stdarg.h>
#include <stdio.h>
/* minprintf: minimal printf with variable argument list */
void minprintf(char *fmt, ...)
{
va_list ap; /* points to each unnamed arg in turn */
char *p, *sval;
int ival;
double dval;
va_start(ap, fmt); /* make ap point to 1st unnamed arg */
for (p = fmt; *p; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
switch (*++p) {
case 'd':
ival = va_arg(ap, int);
printf("%d", ival);
break;
case 'f':
dval = va_arg(ap, double);
printf("%f", dval);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++)
putchar(*sval);
break;
default:
putchar(*p);
break;
}
}
va_end(ap); /* clean up when done */
}
// I added this main below to test K&R minprintf function. It compiled no errors and gave right answer.
int main()
{
int i = 25;
int j = 21;
char str[] = "This is a test";
minprintf("%d, %d, %s\n", i, j, str);
}
minprintf
怎么知道什么时候结束?里面有NULL
吗? K&R 不解释。从我在网上阅读的内容和上面的一些引述来看,可变参数函数不知道在哪里结束,除非你告诉它,比如在省略号前加上一个 int。
是的,里面有一个[=11=]
。查看循环的终止条件:
for (p = fmt; *p; p++) { // look here
// ...
}
当p
不指向[=11=]
值时,*p != '[=14=]'
,即(bool)*p
是true
。在这种情况下,*p
被处理,无论是 %
还是其他。
当p
指向[=11=]
值时,(bool)*p
是false
。循环结束,va_end
被调用。
因此,在扫描完字符串、处理所有 %
说明符后,它会通过检查 字符串结尾 .