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

发布于 2024-09-10 11:09:01 字数 1123 浏览 7 评论 0原文

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

  1. 在调用 foo 时使用最终参数,例如 -1,因此您的函数调用将如下所示:foo(tmp, 1, 2 , 9, -1) 当你在 foo 内部并且 va_arg 调用返回 -1 时,你知道你已经读取了所有函数参数

  2. < p>在 foo 中再添加一个参数,程序员将获得参数总数,因此您将像这样调用 foo: foo(tmp, 5, 1, 2, 3, 4, 5) 或 foo(tmp, 2, 7, 8)

我曾经遵循第一种方式曾经有过以下bug。使用代码:

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 作为最后一个参数并继续搜索创建了一个错误,这花了我一些时间才找到。

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

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

Let's say I have a variadic function foo(int tmp, ...), when calling foo function I need to know how many arguments there are. I'm aware of two ways of finding out how many arguments there are:

  1. Use a final argument when calling foo, like -1, so your function call will be like this: foo(tmp, 1, 2, 9, -1) and when you are inside foo and a va_arg call returns -1 you know you have read all the function arguments

  2. Add one more argument in foo where the programmer will have the total number of arguments, so you will have calls to foo like this: foo(tmp, 5, 1, 2, 3, 4, 5) or foo(tmp, 2, 7, 8)

I used to follow the first way and once had the following bug. With the code:

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

where expr_of_type was a variadic function and was checking if expr(the first argument) was one of the following types (boolexpr_e or new_table_e or nil_e had all type of an enumerated type).
I one accidently wrote:

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

I forgot the comma between nil_e and -1, because nil_e had an enumerated type, nil_e - 1 was a valid expression and because nil_e was not 0 the given variadic function when trying to get expr_of_type arguments didn't find -1 as last argument and continued searching creating a bug which took me some time to find out.

I don't find the second way nice either, because when adding or removing one more argument from a variadic function you need to change the parameter that contains the number of total arguments.

In searching for a better way to use/create variadic functions I found variadic macros which can solve the bug I had when using the first way. But variadic macros are available to C99 standard. I was looking for a better way to use/create variadic functions in C89. Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

榕城若虚 2024-09-17 11:09:01

一般来说,您仍然必须以某种方式传递参数计数,无论是通过哨兵值还是通过显式计数。

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

#define VARARG_SENTINEL (-1)

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

使用 enumconst int 也可以:

enum { VARARG_SENTINEL = -1 };

使用符号常量作为哨兵值也会更好,因为其他原因(更多的自记录,更容易更改基础值)之后)。

In general, you must still pass along the argument count somehow, whether via a sentinel value or via an explicit count.

You could, however, fix your sentinel problem by making a better sentinel. This is one of the reasons why preprocessor macros that expand to negative constants should be surrounded in parentheses:

#define VARARG_SENTINEL (-1)

Then nil_e VARARG_SENTINEL will generate a compilation error.

Using enum or const int would work too:

enum { VARARG_SENTINEL = -1 };

Using a symbolic constant for the sentinel value would be better for other reasons too (more self-documenting, easier to change the underlying value later).

瞎闹 2024-09-17 11:09:01

如果您编译 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;
}

输出:

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

但是,这会阻止您返回值。您可以使用 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;
}

输出:

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

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

编辑:

哎呀,没有仔细阅读OP。对不起!

If your compiling C99, you can use variadic macros to provide variable arguments without having to pass the count explicitely:

#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;
}

Output:

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

This prevents you from returning a value, however. You can return a value with gcc extensions:

#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;
}

Output:

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

But, of course, macros are dead. Long live macros!

EDIT:

Oops, didn't read the OP carefully enough. Sorry!

—━☆沉默づ 2024-09-17 11:09:01

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

struct vararray {
   uint_t n;
   uint_t params[0];
};

void foo(int tmp, struct varray *pVA);

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

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

There's also always the possibility to avoid completely variadic parameters by using a dynamic structure.

struct vararray {
   uint_t n;
   uint_t params[0];
};

void foo(int tmp, struct varray *pVA);

Can even be complexified with a union of structs of different sizes.

We had once an embedded controller with a specific API where we used this kind of approach, a union of fixed sized struct that was passed to the event handler. It had some advantages as specific types could be used and the compiler could better check the types of function parameters because we should not forget that there is no parameter type checking on variadic functions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文