【问题标题】:Variadic function without specifying amount of parameters不指定参数数量的可变参数函数
【发布时间】:2019-11-07 04:07:17
【问题描述】:

我正在研究 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: %d\n", i, num);
    }

    va_end(args);

    return total;
}

int main()
{
    int result = sum(1, 2, 3, 4, 5);
    printf("The result is: %d\n", result);
}

上面的代码只打印:

Value #0: 2
The result is: 2

我认为这是因为for 循环使用第一个参数作为索引的最大值。但是……

我的问题是printf 如果不需要传递要替换的格式化字符串中的参数数量,它是如何工作的? 是不是因为在幕后,C 运行时计算在格式化字符串中声明了多少格式说明符?这是我的猜测。

提前致谢。

【问题讨论】:

    标签: c variadic-functions


    【解决方案1】:

    没有办法知道实际提供了多少参数。

    printf() 从格式字符串中计算出来。格式字符串中的每个% 运算符都对应一个参数(这是一种简化),它处理所需的参数以填充每个参数。

    所以如果你写:

    printf("%d %s\n", intvar, stringvar);
    

    它知道必须有 2 个附加参数:一个用于%d,另一个用于%s

    某些函数使用的另一种方法是使用标记值来指示最后一个参数。

    execl("program", "arg0", "arg1", "arg2", (char *)NULL);
    

    execl() 处理参数直到达到NULL 值。

    【讨论】:

    • 顺便说一句,NULL 的有用且正确的演员阵容可确保通过char*。紫外线
    • 有趣,execl() 的方法似乎很有希望,但是如果我从使用 va_arg 获得的值是 NULL 值,我该如何检查循环内部?我应该和'\0'比较吗?
    • 虽然printf()通过查看格式字符串只知道有多少个参数,但编译器确切知道传递了多少个参数,所以它可以做那种检查和检查软件不能。如果您编写自己的可变参数函数,那么使用编译器特定的修饰来增加外部声明是非常明智的,这样编译器就可以为您进行检查。 GNU 使用 __attribute__() 的变体效果非常好。
    • @CatBrownie "如果我从使用 va_arg 获得的值是 NULL 值,我如何检查循环内部?" 通过将其与 @ 进行比较987654339@。请注意,根据定义,execl() 应该只获得传递的指针。
    • @CatBrownie char *next_arg = va_arg(args, char *); if (next_arg == NULL) ...
    【解决方案2】:

    如果您将可变参数函数称为:

    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)知道如何验证格式字符串的参数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-17
      • 1970-01-01
      • 1970-01-01
      • 2011-01-09
      相关资源
      最近更新 更多