C 函数中的变量参数列表 - 如何正确遍历 arg 列表?

Posted

技术标签:

【中文标题】C 函数中的变量参数列表 - 如何正确遍历 arg 列表?【英文标题】:Variable argument lists in C functions - How to properly iterate through the arg list? 【发布时间】:2010-12-31 19:04:52 【问题描述】:

在以下 C 程序中,我收到警告:

warning #2030: '=' used in a conditional expression.

究竟是什么问题,我该如何避免这种情况?遍历变量参数的正确方法是什么?

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

int Sum(int a, int b, ...)

    int arg;
    int Sum = a + b;

    va_list ap;
    va_start(ap, b);

    while(arg = va_arg(ap, int))
    
        Sum += arg;
    
    va_end(ap);

    return Sum;


int main(int argc, char *argv[])

    printf("%d\n", Sum(1, 2, 4, 8));

    return 0;

【问题讨论】:

您的示例代码已损坏:如果arg == 0 则循环终止,但您从未向Sum() 提供0 参数;如果所有可选参数都具有相同的类型,最好传递一个数组而不是使用可变参数,并做一些宏魔术让它看起来不错:***.com/questions/1375474/variable-arity-in-c/… 好点!但这不会影响问题。你的宏虽然不错,但显然是更好的选择! 【参考方案1】:

你正在做的是惯用的,如果有点丑 C。

不过,为了让编译器知道自己在做什么,您可以将赋值语句包装到一组额外的括号中:

while((arg = va_arg(ap, int)))

这应该会处理警告。

更新:

在赋值周围添加括号似乎不会抑制使用 (PellesC) 的 C99 编译器中的警告。 ——加里·威洛比

什么,没有?然后你需要让测试更明确一点:

while((arg = va_arg(ap, int)) != 0)

应该可以解决问题。它也可以说是更易读。


你会问我“有点丑”是什么意思。

从使用其他语言开始,我习惯于将测试和修改明确分开。您正在对 while 的值进行测试,但同时会产生副作用(即读取下一个参数)。正如我所说,这被认为是很正常的,是的,在 C 中是“惯用的”,因为很多 C 程序员都这样做;我认为在 K&R 中甚至还有类似代码的示例。

根据个人喜好,我可能会将其重写为:

while (1) 
  arg = va_arg(ap, int);
  if (!arg) break;
  ...

这清楚地将分配与测试分开,并让循环作为(潜在的)无限循环独立存在。很多人会认为我的代码更丑;正如我所说,这是个人喜好问题。

【讨论】:

你说得对,我在 while 测试中使用了偷偷摸摸的快捷方式。我目前正在阅读 K&R,整本书都充满了这样的巧妙小捷径。读完这本书后,我现在有点习惯了,实际上非常喜欢它。但是,它是一个很大的但是,它确实使代码更难阅读。我理解人们为什么同时使用这两种方法,而您的代码确实让事情变得更加清晰。 P.S.在赋值周围添加括号似乎不会抑制使用 (PellesC) 的 C99 编译器中的警告。 窍门很适合教科书。我更喜欢编写其他人容易理解的代码。即使我看起来有点防守编码员.. @Gary Willoughby:对失败的提示感到抱歉。我已经用我希望会更成功的内容更新了我的答案。 也可以使用for-loop:for(arg = va_arg(ap, int); arg; arg = va_arg(ap, int)) ...;这将避免警告,但由于代码重复而很难看【参考方案2】:

警告是关于:

while(arg = va_arg(ap, int))

并且在说“你确定你不是故意的:”

while(arg == va_arg(ap, int))

在这种情况下,你没有这样做,所以你可以说:

while( (arg = va_arg(ap, int)) )

但是,您的代码仍然无法运行。如果不以某种方式提供省略号表示的参数数量,就无法使用可变参数函数。例如,printf 使用 % 符号执行此操作:

printf("%s %d %p", x, y, z);

表示省略号表示三个参数。

在您的情况下,您可能可以使用零作为整数列表中的终止值 - 这就是您的循环所暗示的。

【讨论】:

他的代码确实使用零来终止整数列表,所以他需要像这样调用函数:Sum(1, 2, 4, 8, 0); 这不是我在上一段中所说的吗?【参考方案3】:

while(arg = va_arg(ap, int)) 更改为while((arg = va_arg(ap, int)))

警告是因为您在一个语句中分配和检查分配的值。

【讨论】:

【参考方案4】:

函数的另一种写法:

int Sum(int a, ...)

    int sum = 0;
    int current = a;

    va_list args;
    va_start(args, a);

    for(; current; current = va_arg(args, int))
        sum += current;

    va_end(args);

    return sum;

它摆脱了(无用的)第二个命名参数,并将参数指针的增量移出循环条件。

【讨论】:

【参考方案5】:

示例代码

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

int add(int num, ...) 
    va_list vaList;
    int sum = 0;
    va_start(vaList, num);
    for (int i = 0; i < num; ++i) 
        sum += va_arg(vaList, int);
    
    va_end(vaList);
    return sum;


int main() 
    printf("%d\n", add(1, 1));
    printf("%d\n", add(2, 1, 2));
    printf("%d\n", add(3, 1, 2, 3));
    printf("%d\n", add(4, 1, 2, 3, 4));
    printf("%d\n", add(5, 1, 2, 3, 4, 5));
    return 0;

输出: 1

3

6

10

15

【讨论】:

以上是关于C 函数中的变量参数列表 - 如何正确遍历 arg 列表?的主要内容,如果未能解决你的问题,请参考以下文章

c语言中的可变参数

遍历构造函数的参数

DTrace - 如何在返回探针中正确检索初始参数

如何将装饰器中的变量传递给装饰函数中的函数参数?

如何优雅地构造在 C 中遍历数组的长参数列表

C语言 vprintf 函数