我正在学习C编程语言,我发现非常有趣的一件事是可变参数函数的实现。我目前正在尝试打印传递到函数中的每个值,但我只得到了一部分参数。
我尝试运行一些我在网上找到的参数平均数示例以及求和算法(如下所示)。
#include <stdio.h>
#include <stdarg.h>
int sum(int count, ...)
{
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
int num = va_arg(args, int);
total += num;
printf("Value #%d: %dn", i, num);
}
va_end(args);
return total;
}
int main()
{
int result = sum(1, 2, 3, 4, 5);
printf("The result is: %dn", result);
}
上面的代码仅打印:
Value #0: 2
The result is: 2
我认为这是因为for
循环使用第一个参数作为索引的最大值。但。。。
我这里的问题是,如果没有必要在格式化字符串中传递要替换的参数数量,printf
如何工作? 是因为在后台,C 运行时会计算格式化字符串中声明了多少个格式说明符?这是我的猜测。
没有办法找出实际提供了多少参数。
printf()
从格式字符串中找出来。格式字符串中的每个%
运算符都对应于一个参数(这是一种简化),它处理尽可能多的参数来填充每个参数。
所以如果你写:
printf("%d %sn", intvar, stringvar);
它知道必须有 2 个附加参数:一个用于%d
,另一个用于%s
。
某些函数使用的另一种方法是指示最后一个参数的哨兵值。
execl("program", "arg0", "arg1", "arg2", (char *)NULL);
execl()
处理参数,直到到达NULL
值。
如果将可变参数函数调用为:
int result = sum(5 /*count*/, 1, 2, 3, 4, 5);
通过添加初始5
(计数),它将完成您期望的操作,但为了好玩,请尝试使用更大的数字(例如 6 或 10)调用它,看看会发生什么。他们很容易出错。
可变参数函数几乎唯一好的案例是为您的应用量身定制的 printf 变体。 我长期以来最喜欢的是die()
,它采用 printf 样式的格式字符串(和参数),将其发送到标准错误,附加换行符,然后退出程序。
#include <stdlib.h>
#include <stdarg.h>
void die(const char *format, ...)
{
va_list args;
va_start(args, format);
vprintf(stderr, format, args);
va_end(args);
fprintf(stderr, "n");
exit(EXIT_FAILURE);
}
然后将其放在头文件中以使用它:
extern void die(const char *format, ...)
__attribute__((noexit)) // function never exits
__attribute__((printf(1, 2))); // looks like printf, format is arg1
现在你可以打电话给die("Program failed because %s", reason);
,它可以保释程序,而不会有很多麻烦和大惊小怪。由于使用__attribute__
,编译器(至少是GNU)知道如何验证格式字符串的参数。