我正在UNIX环境3版中进行高级编程,我找到了这段代码
err_msg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, 0, fmt, ap);
va_end(ap);
}
和err_doit
是:
static void
err_doit(int errnoflag, int error, const char *fmt, va_list ap)
{
char buf[MAXLINE];
vsnprintf(buf, MAXLINE-1, fmt, ap);
if (errnoflag)
snprintf(buf+strlen(buf), MAXLINE-strlen(buf)-1, ": %s",
strerror(error));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(NULL); /* flushes all stdio output streams */
}
我不理解的是作者为const char
传递指向va_start
的指针的原因。据我所知,你只允许传递省略号部分所代表的参数数量,如下所述:
void f1(int n, ...);
int f2(const char * s, int k, ...);
最右边的参数(省略号之前的参数)起着特殊的作用;标准使用
术语parmN
作为讨论中使用的名称。在前面的示例中,parmN将为n
对于第一种情况,k
用于第二种情况。 传递给此参数的实际参数将是
是省略号部分表示的参数数量。 源:
Stephen Prita的C Primer Plus。
我还检查了用 ISO C标准编写的两个示例:7.15变量参数,我发现他们也将 int
变量传递给{{ 1}}宏。
恐怕是我在某个地方错过了一点。我希望有人告诉我。
答案 0 :(得分:5)
va_start
的第二个参数是var-args参数开始之前的最后一个“真实”参数。
err_msg()
函数只有一个普通参数const char *fmt
指针,所以很明显这是var-args部分之前的最后一个参数,应该传递给va_start()
。
参数last是变量参数列表之前的最后一个参数的名称,也就是调用函数知道类型的最后一个参数。
va_list
的第二个参数的实际类型并不重要。
另请注意,相当常见的printf()
函数很可能就是这样,因为它也只有一个“普通”参数,即格式字符串。
答案 1 :(得分:3)
来自Wikipedia:
然后使用两个参数调用宏
va_start
:第一个是声明类型va_list
的变量,第二个是函数的最后一个命名参数的名称。
或者来自C standard library:
C库宏
void va_start(va_list ap, last_arg)
初始化ap
变量以与va_arg
和va_end
宏一起使用。last_arg
是传递给函数的最后一个已知固定参数,即。省略号之前的参数。
关键是您需要...
之前的标准参数才能将其提供给va_start
。 va_start
将仅使用它来检索补充参数(纯C烹饪)。
最后一个标准参数不相关,但可能想要用它来知道补充参数的数量。这也是一个使用示例,我认为这是您的来源所做的:一个使用示例。
答案 2 :(得分:3)
假设您已正确引用该书,该声明:
传递给此参数的实际参数将是省略号部分表示的参数数量。
不正确,或至少具有误导性。
在您的具体示例中:
void f1(int n, ...);
int f2(const char * s, int k, ...);
省略号恰好是int
之前的最后一个命名参数,其值可能用于指定与...
对应的参数数量。
这是一个合理的约定(尽管你仍然需要指定那些参数的 types ),但语言不需要它。
va_start()
的第二个参数始终是最后一个命名参数的名称(不是值)。由于va_start
是宏,而不是函数,因此通过值传递所有参数的通常规则不适用。在内部,va_start
使用参数名称来确定内存(或寄存器)中可以找到其他参数值的位置。 (没有可移植的方法来执行此操作,因此标准未指定va_start
的实际定义;必须为每个编译器自定义它。)
所有真正需要的是该函数必须能够以某种方式找出 调用者传递的参数的数量和类型。 printf
通过解析格式字符串来完成此操作。 execl()
(POSIX函数,未由ISO C定义)通过假设所有参数都是char*
类型并收集参数直到找到空指针来完成它。其他机制也是可能的。如果调用者传递不一致的参数,则行为未定义;调用者无法检测到错误。
即使在您的示例中,va_start
的第二个参数是而不是“省略号部分表示的参数数量”。这是参数的名称。即使参数值恰好是3
,将文字3
传递给va_start
也是不正确的。该函数将通过读取命名参数的值而不是与va_start
相关的任何内容来确定参数的数量。
va_start
中定义了<stdarg.h>
宏的语义以及{{1}}中定义的其他项目。
答案 3 :(得分:0)
C标准只要求省略号之前的最后一个参数是默认提升的,而不是用函数或数组类型声明(尽管允许使用函数指针和指针类型,所以这只是一个语法限制)。
对于f1
和f2
,最后一个命名参数包含传递的参数数量,尽管这完全是传统的。
标准库中以不同方式处理的示例是printf
(参数的数量(和类型)由格式字符串确定)和execl
(参数列表的末尾是用NULL
指针标记。