【问题标题】:How can I make a function with optional arguments in C?如何在 C 中创建带有可选参数的函数?
【发布时间】:2013-10-06 18:00:03
【问题描述】:

最近在写一个打开文件的程序时遇到一个问题。

让我清楚地解释我的问题。这里我以open 通话为例。

创建文件:

open("file_name", O_CREAT, 0766); //passing 3 parametrs

打开文件:

open("file_name", O_RDWR); //only 2 arguments.

然后我清楚地观察到了这一点,它也适用于main()

main(void) //worked
main(int argc, char **argv); //worked
main(int argc) //worked and it's doesn't give an error like "too few arguments". 
main() //worked 

那么我们如何创建这些可选参数呢?编译器究竟如何验证这些原型?如果可能,请写一个示例程序。

【问题讨论】:

标签: c function gcc gnu


【解决方案1】:

open 函数被声明为可变参数函数。它看起来像这样:

#include <stdarg.h>

int open(char const * filename, int flags, ...)
{
    va_list ap;
    va_start(ap, flags);

    if (flags & O_CREAT)
    {
        int mode = va_arg(ap, int);
        // ...
    }

    // ...

    va_end(ap);
}

除非您表明它们确实存在,否则不会使用进一步的参数。

printf 使用相同的结构。

手册并不总是明确说明这一点,因为唯一可能的两个签名是(char const *, int)(char const *, int, int),所以告诉你这个函数实际上接受可变参数没有什么意义。 (你可以通过编译类似open("", 1, 2, 3, 4, 5, 6)的东西来测试它。)

【讨论】:

  • 但他们如何验证这些。它没有给出任何论点,例如“论点太少”。
  • @SGG:与printf 验证您是否传递了正确数量和类型的参数的方式相同。
  • @KerrekSB:抱歉,我没说清楚。看到有一个函数接受一个参数或什么都没有(void)。我的意思是,如果我调用函数()或函数(10),函数应该被验证。你能写出那个函数的定义吗?
  • @SGG:你不能。您必须记录您的合同并依靠用户遵守合同。就此而言,您不能有零个参数,对于可变参数样式的函数,您必须至少有一个。
  • open 函数早于 1989 年 ANSI C 标准引入的 , ... 语法。 open 的早期版本只是记录了两种不同的形式,并依赖于弱类型检查来允许它们(在没有原型的情况下,编译器不会诊断带有错误数量的参数的调用,而原型不是直到 1989 年才推出)。随着 ANSI C 标准的引入,open 的定义被更新为使用, ...(并且可能在内部使用&lt;stdarg.h&gt;)。
【解决方案2】:

在所有情况下,可变参数函数必须能够以某种方式从固定参数中确定有多少可变参数。例如,printf() 系列函数使用格式字符串来确定参数的数量和类型。 execl() 函数使用标记(空指针)来标记参数列表的结尾。可以使用计数而不是哨兵(但如果您要这样做,则非可变参数函数中的计数和数组可能会与计数和参数列表)。 open() 函数使用标志位之一来确定模式是否应该存在 - 请参阅 Kerrek SBanswer

main() 函数是一个特例。实现(编译器)禁止为其声明原型,必须至少接受以下两种形式:

int main(int argc, char **argv);
int main(void);

或它们的等价物。它也可以接受其他形式;请参阅What's the use of the third environment variable in the C main()? 了解一种常见形式。在 C 而不是 C++ 中,标准编译器可以记录其他返回类型 - 并且 Microsoft 已将 void 记录为从 VS 2008 开始的有效返回类型。

因为没有为main() 提供实现的原型,编译器不能正式拒绝main() 的任何声明/定义,尽管它可能会通过它不识别的形式的评论(GCC 确实评论@例如,不返回 int 类型的 987654333@ 函数。

【讨论】:

  • 实际上编译器可以拒绝它不接受的main 形式。替代形式是实现定义的,这意味着编译器必须附有列出它们的文档。定义既不是两种标准形式之一也不是当前编译器的实现定义形式的程序具有未定义的行为(省略,因为标准没有定义行为)(5.1.2.2.1)。拒绝翻译单元是对未定义行为 (3.4.3) 的可接受响应。
猜你喜欢
  • 2012-03-21
  • 2011-06-20
  • 2011-12-03
  • 2015-04-08
  • 1970-01-01
  • 2020-09-20
  • 1970-01-01
  • 2021-03-28
相关资源
最近更新 更多