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

在C89中使用变量函数而不传递参数数或最终参数?,c,enums,c89,variadic-functions,C,Enums,C89,Variadic Functions,假设我有一个变量函数foo(inttmp,…),当调用foo函数时,我需要知道有多少个参数。我知道有两种方法可以找出有多少论点: 在调用foo时使用最后一个参数,如-1,因此函数调用如下:foo(tmp,1,2,9,-1)当您在foo内部并且va_arg调用返回-1时,您知道您已经读取了所有函数参数 在foo中再添加一个参数,程序员将拥有参数总数,因此您将有如下对foo的调用:foo(tmp,5,1,2,3,4,5)或foo(tmp,2,7,8) 我曾经遵循第一种方法,曾经有过如下错误。使用代码

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

  • 在调用foo时使用最后一个参数,如-1,因此函数调用如下:
    foo(tmp,1,2,9,-1)
    当您在foo内部并且va_arg调用返回-1时,您知道您已经读取了所有函数参数

  • 在foo中再添加一个参数,程序员将拥有参数总数,因此您将有如下对foo的调用:
    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_类型参数时,给定的变量函数没有找到-1作为最后一个参数,并继续搜索,创建了一个bug,我花了一些时间才发现

    我也不认为第二种方法很好,因为当从变量函数中添加或删除一个参数时,需要更改包含总参数数的参数


    在寻找更好的使用/创建可变函数的方法时,我发现它可以解决我在使用第一种方法时遇到的错误。但变量宏可用于C99标准。我在寻找一种更好的方法来使用/创建C89中的可变函数。有什么想法吗?

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

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

    #define VARARG_SENTINEL (-1)
    
    然后
    nil_e VARARG_SENTINEL
    将生成编译错误


    使用
    enum
    const int
    也可以:

    enum { VARARG_SENTINEL = -1 };
    

    出于其他原因,使用符号常量作为sentinel值也会更好(更自我记录,以后更容易更改基础值)。

    通过使用动态结构,也可以避免完全可变的参数

    struct vararray {
       uint_t n;
       uint_t params[0];
    };
    
    void foo(int tmp, struct varray *pVA);
    
    甚至可以通过不同大小的结构的
    联合
    进行复杂化


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

    如果您编译C99,您可以使用可变宏提供可变参数,而无需显式传递计数:

    #include <stdio.h>
    #include <stdarg.h>
     
    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;
    }
    
    但是,这会阻止您返回值。您可以使用gcc扩展返回一个值:

    #include <stdio.h>
    #include <stdarg.h>
     
    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;
    }
    
    但是,当然,宏已经死了。宏万岁

    编辑:
    哎呀,我没有仔细阅读这篇文章。对不起

    当忘记逗号时,使用
    enum
    const int
    会导致同样的问题?不,不会。如果你有
    const int VARARG_SENTILEL=-1
    当您忘记逗号时,会出现类似于nil_e VARARG_SENTINEL的内容,这会导致编译错误。使用
    enum{VARARG_SENTINEL=-1}时也会发生同样的情况。当忘记逗号时,使用这两种解决方案就像使用无效表达式的
    number1 number2
    (例如
    2 3
    ),但当使用-1时,您将使用有效表达式的
    number1
    #include <stdio.h>
    #include <stdarg.h>
     
    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