【问题标题】:variadic functions with different types of arguments in cc中具有不同类型参数的可变参数函数
【发布时间】:2015-11-16 18:43:43
【问题描述】:

我想知道是否可以在 C 中创建一个接受不同类型参数的可变参数函数。即

void fillDatabase(char* name, int age){
  writeToDatabase(name, age);
}

int main(){
  fillDatabase("Paul", 19);
  fillDatabase("Herbert");
}

在这里,我试图用姓名和年龄填充数据库。但也可以只用名字而不用年龄来填充它。所以我想知道我是否可以为此使用相同的功能,或者我是否必须编写两个不同的功能?

Online 我只能找到具有相同类型参数的可变参数函数的示例。

提前致谢!

【问题讨论】:

  • 我认为您混淆了可变参数函数和函数重载的概念。
  • printf 是一个可变参数函数,可以接受不同类型的参数...请注意,您需要提供一些显式提供参数类型的方法(printf-like 函数通过格式字符串)。
  • 只需写两个函数。编写可变参数函数,使用structunion 或任何其他技巧只会自找麻烦。说真的。
  • 伙计们,如果有人要你帮他在脚上开枪,你不会给他枪……至少不是没有充分理由……跨度>
  • 好的,谢谢,我就用两个函数

标签: c function variadic variadic-functions


【解决方案1】:

在 C99 中,可变参数函数使用 stdarg(3) 操作(通常实现为扩展为编译器特定神奇东西的宏,例如 GCC 内建函数)。第一个参数应该有一些固定的已知类型,并且通常确定如何获取其他参数;在当今的几个 ABI 中,可变参数函数参数传递不使用寄存器,因此效率低于固定参数函数调用。

在你的情况下,你最好有几个功能。为获得灵感,sqlite 有several open functions

请注意,POSIX 使用一个 last 可选参数定义了一些函数,特别是 open(2)。这可能实现为具有无法提供 last 参数的函数。

您可以阅读ABI 规范和特定于您的实现的calling conventions

顺便说一句,缺少函数重载(如在 C++ 中)可以理解为 C 的一个优势(函数名称——这是 linker 唯一关心的东西,至少在 Linux 和 Unix 上——决定了它的签名) .因此 C++ 实际上需要name mangling。另请阅读 dynamic linkingdlopen

【讨论】:

  • 传统上,没有提供open() 的原型,因此程序员可以根据需要使用两个或三个参数调用它。如今,open() 被声明为 int open(const char*, int, ...); 并使用可变参数。
  • 在实践中,并非如此。
  • 那么POSIX在这方面错了吗?
  • 不,我认为open实现 不使用va_start,但在musl-libc 中使用open.c va_start。所以我错了。 IIRC,一些旧版本的 Glibc 没有这样做(但我也可能错了)
  • 正如我所说,open() 的旧实现只是没有提供原型,而是使用 K&R 风格的定义和三个参数定义了 open()。参数计数不匹配是未定义的行为,但只要您从不接触未提供的参数,就可以在典型的 Unices 上正常工作。
【解决方案2】:

查看 va_list、va_start 等的手册页。

您使用 ... 序列声明您的函数原型以表示变量 args:

void myfunction(FILE *, *fmt, ...);

例如,我们声明一个 va_list 类型的结构(假设您包含 stdarg):

struct va_list l;

在...之前使用参数初始化列表 - 我们会将 *fmt 指针传递给 va_start:

va_start(l, fmt);

现在,我们可以调用 va_arg 来获取一个值...如果我们正在处理一个格式字符串,我们可能会有一个类似这样的循环:

while (*fmt) {
    /* Look for % format specifier... */
    if (*fmt == '%') {
        /* Following character can be c for char, i for int or f for float. */
        // Opps - advance to char following opening %...
        fmt++;

        if (*fmt == 'c')
            fprintf(stream, "%c", (char) va_arg(l, int));
        else if (*fmt == 'i')
            fprintf(stream, "%i", (int) va_arg(l, int));
        else if (*fmt == 'f')
            fprintf(stream, "%f", (float) va_arg(l, double));
        ... and so on...

您依靠 % 后面的字符来告诉您在相应参数中期望的数据类型。建议您阅读 va_list 的手册页。

【讨论】:

  • 那么您将如何将所有这些知识应用于特定问题?
  • 嗯...我真的不知道具体问题的来龙去脉,我不是 OP。让 OP 决定我的答案是否足以满足他/她的目的,如果它很好......如果不是那么抱歉,请继续寻找。我真的不知道你想要什么或期望我告诉你...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 2020-01-23
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多