【问题标题】:How to know last argument of va_list?如何知道 va_list 的最后一个参数?
【发布时间】:2014-05-14 06:23:37
【问题描述】:

我正在阅读有关如何将可选参数传递给函数的信息。但我无法理解那些。当我看到示例时,它们令人困惑且有点复杂。所以我只是从一个非常简单的程序开始,到目前为止我已经理解了。

下面的程序只是打印变量。

void print(int x, ...)
{
        va_list ap;
        int i = 4;     // I know I'm passing only 4 opt variables. 
        int num;

        va_start(ap, x);
        while(i--) {         // How to know how many variables came in real time?
                num = va_arg(ap, int);
                printf("%d\n", num);
        }
        va_end(ap);
        return;
}

int main()
{
        print(1,2,3,4,5);
        return 0;

}

我不知道上面的程序是否正确。但它正在工作。当我将i 值更改为5 打印垃圾时。如何知道我得到了多少个参数(比如main 中的argc)?

【问题讨论】:

  • 以同样的方式知道传递给函数的数组中有多少项;你自己跟踪它,因为 C 不会为你做这件事。
  • How to know how many arguments I got (like argc in main)? - 你没有。你将它与参数一起传递。也许作为第一个论点。请不要尝试遵循您从以下答案中获得的“很棒”的想法。他们真的,真的很可怕。

标签: c optional-parameters variadic-functions


【解决方案1】:

没有办法知道从可变参数函数内部传递了多少参数,这就是为什么像 printf 这样的函数使用特殊格式的字符串来告诉函数期望多少参数。

另一种方式当然是将“额外”参数的数量作为第一个参数传递,比如

print(4, 1, 2, 3, 4);

或者有一个不能在列表中作为最后一个参数的特殊值,比如

print(1, 2, 3, 4, -1);

您还必须注意,您传递给函数的最后一个非 va 参数(在您的情况下名为 num 的参数)不包含在 va_list 中,因此在您的情况下使用显示带有4 的代码作为硬编码的参数数量,您将仍然打印垃圾,因为您将1 传递给num 参数,然后传递三个va_list 参数。

还要小心,因为您使用 num 作为参数和局部变量名。

【讨论】:

  • 嗯...知识++; :-)
  • 假设我有 30-40 个数字作为可选参数传递。所以我每次都有数?如果-1 是其中的一个参数呢?
  • @SGG 如果没有可用作“列表末尾”标记的特定值,则必须将计数作为第一个(非va_list)参数传递。
  • 如果我将此代码提供给人们,并且如果我说请数一数您传递的数字并将其作为第一个变量传递是不好的。
  • @SGG 也许不会,但你别无选择。
【解决方案2】:

你可以去NARGS macro看看

适应您的代码:

#include <stdio.h>
#include <stdarg.h>

#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)

#define print(...) fnprint(NARGS(__VA_ARGS__), __VA_ARGS__)

void fnprint(int n, ...)
{
        va_list ap;
        int num;

        va_start(ap, n);
        while (n--) {
                num = va_arg(ap, int);
                printf("%d\n", num);
        }
        va_end(ap);
        return;
}

int main(void)
{
        print(1, 2, 3, 4, 5);
        return 0;
}

编辑: 如果要对宏和函数使用相同的名称,请使用 () 来阻止预处理器扩展函数定义:

#define print(...) print(NARGS(__VA_ARGS__), __VA_ARGS__)

void (print)(int n, ...) /* Note () around the function name */
{
  ...

编辑 2: 使用复合文字(std 99)和 sizeof 的另一种(丑陋)方法(但不限制 args 的数量):

#include <stdio.h>
#include <stdarg.h>

#define print(...) print(sizeof((int []) {__VA_ARGS__}) / sizeof(int), __VA_ARGS__)

void (print)(int n, ...)
{
    va_list ap;
    int num;

    va_start(ap, n);
    while (n--) {
        num = va_arg(ap, int);
        printf("%d\n", num);
    }
    va_end(ap);
    return;
}

int main(void)
{
    print(1, 2, 3, 4, 5);
    return 0;
}

print 扩展为:

print(sizeof((int []) {1, 2, 3, 4, 5}) / sizeof(int), 1, 2, 3, 4, 5);

但是像print(1, 2, 3, a_var++, 4, 5);print(1, 2, some_func_returning_int(), 4, 5); 这样的结构会计算a_var++some_func_returning_int() 两次,这是个大问题。

【讨论】:

  • fnprint 重命名为 print
  • 对不起,我没有看到#define print(...) fnprint(NARGS.. 行。我就是这么说的。现在好啦。并且工作了。但我不明白 NARGS 究竟做了什么。
  • @SGG,感谢您检查此答案,但请注意 print 仅限于 N args,Joachim 的答案 (pass the number of "extra" arguments as the first argument) 没有限制
  • 您的代码仅支持 9 个数字。如果超过 9 个数字,就会出现分段错误。对吗?
  • @SGG 第一种方法的参数数量有限,秒只取特定类型。下面是解决这两个问题的办法:stackoverflow.com/a/20993120/2327831(无耻的自我推销)
【解决方案3】:

int 和字符串的另一种丑陋方式:

#include <stdio.h>
#include <stdarg.h>

#define print(...) fnprint("{" # __VA_ARGS__ )

fnprint(char *b) {
    int count = 0, i;
    if (b[1] != '\0') {
        for (i =2; b[i]; i++) {
            if (b[i] == ',')
               count++;
        }
        count++;
    }
    printf("\ncount is %i\n", count);
}

int main(void)
{
        print();
        print("1", "2");
        print(1, 2, 3, 4, 5);
        return 0;
}

【讨论】:

  • 您以char 接收它们。如果我想操作接收到的变量,我必须将它们转换为特定类型。我的代码会很大
  • 这个函数只是用来计数的实用程序,然后将这个 val 和 VA_ARGS 分别传递给其他函数:#define print(...) PRINT( COUNTINGFUNCTION("{" # VA_ARGS ), VA_ARGS )
猜你喜欢
  • 1970-01-01
  • 2018-11-08
  • 2020-11-11
  • 1970-01-01
  • 1970-01-01
  • 2011-08-21
  • 2019-08-09
  • 2020-06-14
  • 1970-01-01
相关资源
最近更新 更多