在C89中使用可变参数函数而不传递参数或最终参数?

假设我有一个可变函数foo(int tmp, ...) ,当调用foo函数时我需要知道有多少个参数。 我知道有两种方法可以找出有多少参数:

  1. 在调用foo时使用最后一个参数,比如-1,所以你的函数调用将是这样的: foo(tmp, 1, 2, 9, -1) ,当你在foo内部并且va_arg调用返回-1时你知道你已阅读所有函数参数

  2. 在foo中再添加一个参数,程序员将拥有参数总数,因此你可以像这样调用foo: foo(tmp, 5, 1, 2, 3, 4, 5) foo(tmp, 2, 7, 8) foo(tmp, 5, 1, 2, 3, 4, 5)foo(tmp, 2, 7, 8)

我曾经遵循第一种方式,曾经有过以下错误。 随着代码:

 expr_of_type(expr, boolexpr_e, newtable_e, nil_e, -1) 

其中expr_of_type是一个可变参数函数,并检查expr(第一个参数)是否是以下类型之一(boolexpr_e或new_table_e或nil_e具有所有类型的枚举类型)。 我一个人意外地写道:

 expr_of_type(expr, boolexpr_e, newtable_e, nil_e -1) 

我忘记了nil_e和-1之间的逗号,因为nil_e有一个枚举类型,nil_e – 1是一个有效的表达式,因为nil_e不是0,当尝试获取expr_of_type参数时,给定的可变参数函数没有找到-1作为最后一个参数继续搜索创建一个花了我一些时间才发现的bug。

我也没有找到第二种方式,因为当从可变参数函数中添加或删除一个参数时,您需要更改包含总参数数量的参数。

在寻找更好的使用/创建可变参数函数的方法时,我发现了可变元宏 ,它可以解决我在使用第一种方式时遇到的错误。 但是可变参数宏可用于C99标准。 我一直在寻找一种在C89中使用/创建可变参数函数的更好方法。 有任何想法吗?

通常,您仍然必须以某种方式传递参数计数,无论是通过标记值还是通过显式计数。

但是,您可以通过制作更好的哨兵来解决您的哨兵问题。 这是扩展为负常量的预处理器宏应该用括号括起来的原因之一:

 #define VARARG_SENTINEL (-1) 

然后nil_e VARARG_SENTINEL将生成编译错误。

使用enumconst int也可以:

 enum { VARARG_SENTINEL = -1 }; 

对于哨兵值使用符号常量也会因其他原因更好(更多自我记录,以后更容易更改基础值)。

通过使用动态结构,总是有可能避免完全可变参数。

 struct vararray { uint_t n; uint_t params[0]; }; void foo(int tmp, struct varray *pVA); 

甚至可以通过不同大小的结构union来进行复杂化。

我们曾经有一个带有特定API的嵌入式控制器,我们使用这种方法,一个固定大小的structunion ,传递给事件处理程序。 它具有一些优点,因为可以使用特定类型,编译器可以更好地检查函数参数的类型,因为我们不应该忘记在可变参数函数上没有参数类型检查。

如果编译C99,则可以使用可变参数宏来提供变量参数,而不必明确地传递计数:

 #include  #include  void _foo(size_t n, int xs[]) { for(int i=0 ; i < n ; i++ ) { int x = xs[i]; printf("%d\n", x); } } #define foo(arg1, ...) do { \ int _x[] = { arg1, __VA_ARGS__ }; \ _foo(sizeof(_x)/sizeof(_x[0]), _x); \ } while(0) int main() { foo(1, 2, 3, 4); foo(-1, -2, -3, -4, -5, -6, -7); return 0; } 

输出:

 1 2 3 4 -1 -2 -3 -4 -5 -6 -7 

但是,这会阻止您返回值。 您可以使用gcc扩展名返回值:

 #include  #include  int _foo(size_t n, int xs[]) { int i; for(i=0 ; i < n ; i++ ) { int x = xs[i]; printf("%d\n", x); } return n; } #define foo(arg1, ...) ({ \ int _x[] = { arg1, __VA_ARGS__ }; \ _foo(sizeof(_x)/sizeof(_x[0]), _x); \ }) int main() { int x = foo(1, 2, 3, 4); printf("foo returned %d\n", x); x = foo(-1, -2, -3, -4, -5, -6, -7); printf("foo returned %d\n", x); return 0; } 

输出:

 1 2 3 4 foo returned 4 -1 -2 -3 -4 -5 -6 -7 foo returned 7 

但是,当然,宏已经死了。 万岁的宏!

编辑:

糟糕,没有仔细阅读OP。 抱歉!