C语言 C 函数中的可选参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27795767/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:40:12  来源:igfitidea点击:

Optional arguments in C function

cfunctionarguments

提问by Eray CANLI

In a C function, I want to check if an input argument ('value' in my case) is presented or not.

在 C 函数中,我想检查是否存在输入参数(在我的情况下为“值”)。

i.e.:

IE:

void Console(char string[], int32_t value)
{
    // write string here
    // write value here, if it exists
}

When used if(value != NULL)statement, my Console() function sends 4096

当使用if(value != NULL)语句时,我的 Console() 函数发送 4096

How can I check and act based on argument existence?

如何根据参数存在进行检查和操作?

回答by Basile Starynkevitch

Optional arguments are generally not allowed in C (but they exist in C++and in Ocaml, etc...). The only exception is variadic functions(like printf).?

C 中通常不允许使用可选参数(但它们存在于C++Ocaml等中...)。唯一的例外是可变参数函数(如printf)。?

Historically, the open(2)function from POSIX accepted in some cases an optional third argument (at the time it was defined - in the 1970s and 1980s -, the calling conventionspractically pushed arguments on the call stack, so ignoring that argument was simple to implement). If you look today at recent implementation of that openfunction in free software libc implementations on Linux, such as musl-libc, you see in its src/fcntl/open.cthat it uses the <stdarg.h>variadic facilities(which are often implementedas compiler builtins).

从历史上看,来自 POSIX的open(2)函数在某些情况下接受可选的第三个参数(在定义它时 - 在 1970 年代和 1980 年代 -调用约定实际上将参数推送到调用堆栈上,因此忽略该参数很简单来实施)。如果您今天在openLinux 上的自由软件 libc 实现中查看该函数的最新实现,例如musl-libc,您会在其src/fcntl/open.c 中看到它使用<stdarg.h>可变参数设施通常作为编译器内置程序实现) .

BTW, you could define some macros to fill the "missing" arguments, so if you have

顺便说一句,你可以定义一些宏来填充“缺失”的参数,所以如果你有

  void console(const char*, int32_t);

you might also

你也可能

  #define console_plain(Msg) console((Msg),0)

and that could be instead some inline function in some header, e.g.

这可能是某些标题中的一些内联函数,例如

  static void inline console_plain (const char*msg) 
  { console(msg, 0); }

then use console_plain("hello here")elsewhere

然后console_plain("hello here")在别处使用

Then your variadic function should define how and what arguments are allowed (after a non-empty sequence of fixedarguments). And use stdarg(3)to get these variadic (actual) arguments.

然后你的可变参数函数应该定义如何以及允许什么参数(在一个非空的固定参数序列之后)。并使用stdarg(3)获取这些可变参数(实际)参数。

The actual arguments are known mostly at compile-time, not at run-time. So you need a convention which often defines which variadic argument are permitted from the required fixed arguments. In particular, you have no way to test that an argument is present (that information is lost at runtime).

实际参数主要在编译时知道,而不是在运行时知道。所以你需要一个约定,它通常定义从所需的固定参数中允许哪个可变参数。特别是,您无法测试参数是否存在(该信息在运行时丢失)。

BTW, with variadic functions you generally lose the typechecking that most C compilers provide (at least when you enable all warnings, e.g. gcc -Wall -Wextra). If using GCCyou might have some function __attribute__-s(like format, sentinel, ....) in the prototype to assist that. You could even customize gccwith the obsolete MELT, or in 2019 with your GCC plugin, to add your own attributes doing their own type checking.

顺便说一句,使用可变参数函数,您通常会丢失大多数 C 编译器提供的类型检查(至少当您启用所有警告时,例如gcc -Wall -Wextra)。如果使用GCC,您可能会在原型中使用一些函数__attribute__-s(例如format, sentinel, ....)来帮助实现这一点。您甚至可以gcc使用过时的MELT 进行自定义,或者在 2019 年使用您的GCC 插件进行自定义,以添加您自己的属性进行自己的类型检查。

How can I check and act based on argument existence?

如何根据参数存在进行检查和操作?

With current usual calling conventions (e.g. study the x86-64 ABI) you generally cannot do that (without using variadic functions).

使用当前通常的调用约定(例如研究x86-64 ABI),您通常无法做到这一点(不使用可变参数函数)。

回答by Edenia

This sounds as if you are trying to use hackery to potentially facilitate yourself or whomever opened your source code "in write mode". I say "in write mode" (not read/write) because such code is very hard to read, because of all the hidden code, because of the magical macros you would need. C is not a smart language. The compilers might be smart, but the language semantics are rather pedantic. You should strictly comply with the rules, otherwise you are risking to make bad software or worse - not working at all.

这听起来好像您正在尝试使用hackery来潜在地促进您自己或“以写入模式”打开源代码的任何人。我说“在写模式下”(不是读/写)是因为这样的代码很难阅读,因为所有隐藏的代码,因为你需要神奇的宏。C 不是一种智能语言。编译器可能很聪明,但语言语义却相当迂腐。你应该严格遵守规则,否则你就有可能制作出糟糕的软件或更糟的——根本无法工作。



The "correct" way to do this, without creating a whole new programming language is to provide two arguments, and when the second should not be used, it should either be passed as NULLif it is a pointer or some excluding number such as -1if it is a numeral type.

“正确”的方式做到这一点,而无需创建一个全新的编程语言是提供两个参数,而当第二不宜使用,要么原样传递NULL,如果它是一个指针或一些不包括数量,例如-1,如果它是数字类型。

Another approach is to create two separate functions with hinting names such as: consoleand console_full. Respectively with one and two arguments.

另一种方法是使用提示名称创建两个单独的函数,例如: consoleconsole_full。分别用一、二参数。

But if you are still not comfortable with the aforementioned approaches, you can include stdarg.hto do it for you.

但是,如果您仍然对上述方法不满意,则可以包括stdarg.h为您做。

void Console (char *string, ...) /* Note the ... */
{
    va_list param;
    int32_t optValue = (-1); /* -1 would indicate optValue is to be ignored */

    // write string here

    va_start(param, string);

    optValue = va_arg(param, int32_t);

    if(optValue != (-1))
    {
        /* Work with `optValue` */
    }

    va_end(param);
}

Which way is not good, because you don't know the types of the additional arguments, neither you know how many are they. To know those things, you should do as printf-alike functions do, parse specific tokens inside the string that suggest an argument exists and what type of argument it is or at least just use a const variable counter of arguments. You can macrofy further that the counting of arguments is automatic.

哪种方式不好,因为您不知道附加参数的类型,也不知道它们有多少。要知道这些事情,您应该像 printf 类似的函数那样做,解析字符串中表明存在参数以及它是什么类型的参数的特定标记,或者至少只使用参数的 const 变量计数器。您可以进一步宏化参数的计数是自动的。



Update:

更新:

You don't really needstdarg to use variadic functionality in C as this is a built-in feature. The C pre-processor can also expand variadic arguments (Although probably not in all versions). The libary stdarg provides those helpful macros, but you can implement them on your own as well. Note that not using a standard library is almost always the wrong thing. Just grab the address of the first (reference) variable, advance it with the size of the pointer and you have the address of the next argument (presumably). Something like:

你真的不需要stdarg 在 C 中使用可变参数功能,因为这是一个内置功能。C 预处理器还可以扩展可变参数(尽管可能不是在所有版本中)。libary stdarg 提供了那些有用的宏,但您也可以自己实现它们。请注意,不使用标准库几乎总是错误的。只需获取第一个(引用)变量的地址,用指针的大小推进它,您就可以得到下一个参数的地址(大概)。就像是:

#include <stdio.h>

#define INIT_VARARG(x,ref)          void* x = &ref
#define GET_NEXT_VARARG(ptr,type)   (*((type* )(ptr+=sizeof(ptr))))

void func (int ref, ...) // ref must be the number of int args passed.
{
    INIT_VARARG(ptr,ref);
    int i;

    for(i = 0; i < ref; i++)
    {
        printf("[%i]\n", GET_NEXT_VARARG(ptr, int));
    }
}

int main (void)
{
    func(3, 10, 15, 20);

    return 0;
}

You may use this for experimental purposes only. A good, secure and well-designed C code should not have such macros and should use stdarg instead.

您只能将其用于实验目的。一个好的、安全的和设计良好的 C 代码不应该有这样的宏,而应该使用 stdarg。

回答by M Oehm

If you want to distinguish between function calls that take either one or two arguments, you can use macros.

如果要区分带有一个或两个参数的函数调用,可以使用宏。

While you can reproduce your desired behaviour, there are some things to note:

虽然您可以重现您想要的行为,但有一些事项需要注意:

  • The macro implentation hides the overloading to casual readers of your code who can't see that Consoleis a macro. C is much about seeing the details, so if you have two different functions, they should probably get different names, maybe cons_strand cons_str_int.

  • The macro will generate a compiler error if you pass more than two arguments or if the arguments are not compatible with the required types C string and int. Which is actually a good thing.

  • Real variadic functions like printfthat use the interface from <stdarg.h>must be able to derive the types and number of variadic arguments. In printf, this is done via the %format specifiers. The macro can switch between different implementations based on the number of arguments alone.

  • 宏实现隐藏了代码的重载,他们看不到Console宏。C 非常注重查看细节,因此如果您有两个不同的函数,它们可能应该有不同的名称,也许cons_strcons_str_int

  • 如果您传递两个以上的参数,或者这些参数与所需的 C string 和 int 类型不兼容,则宏将生成编译器错误。这实际上是一件好事。

  • printf使用 from 接口的真正的可变参数函数<stdarg.h>必须能够派生可变参数的类型和数量。在 中printf,这是通过%格式说明符完成的。宏可以根据参数的数量在不同的实现之间切换。

Anyway, here's an implementation. Proceed with caution.

无论如何,这是一个实现。谨慎行事。

#include <stdlib.h>
#include <stdio.h>

#define NARGS(...) NARGS_(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define NARGS_(_5, _4, _3, _2, _1, N, ...) N

#define CONC(A, B) CONC_(A, B)
#define CONC_(A, B) A##B

#define Console(...) CONC(Console, NARGS(__VA_ARGS__))(__VA_ARGS__)



void Console1(const char string[])
{
    printf("%s\n", string);
}

void Console2(const char string[], int32_t value)
{
    printf("%s: %d\n", string, value);
}

int main()
{
    Console("Hello");
    Console("Today's number is", 712);

    return 0;
}