C 默认参数

发布于 2024-08-05 21:51:39 字数 27 浏览 6 评论 0原文

有没有办法为 C 中的函数指定默认参数?

Is there a way to specify default arguments to a function in C?

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

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

发布评论

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

评论(24

悸初 2024-08-12 21:51:39

哇,这里的每个人都是如此悲观主义者。答案是肯定的。

这并不简单:到最后,我们将拥有核心函数、支持结构、包装函数和宏
围绕包装函数。在我的工作中,我有一组宏来自动化这一切;一次
如果您了解了流程,那么您就可以轻松执行相同的操作。

我已在其他地方写过此内容,因此这里有一个详细的外部链接来补充此处的摘要: http://modelingwithdata .org/arch/00000022.htm

我们希望变成

double f(int i, double x)

一个采用默认值(i=8,x=3.14)的函数。定义一个伴随结构:

typedef struct {
    int i;
    double x;
} f_args;

重命名您的函数 f_base,并定义一个设置默认值和调用的包装函数
基础:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

现在添加一个宏,使用 C 的可变参数宏。这样用户就不必知道他们是
实际上填充一个 f_args 结构并认为它们正在做通常的事情:

#define f(...) var_f((f_args){__VA_ARGS__});

好的,现在以下所有操作都可以工作:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

检查有关复合初始值设定项如何为确切规则设置默认值的规则。

有一点不起作用:f(0),因为我们无法区分缺失值和缺失值
零。根据我的经验,这是需要注意的事情,但可以按照以下方式处理:
需求出现了——有一半的时间你的默认值实际上是零。

我在写这篇文章时遇到了麻烦,因为我认为命名参数和默认值
确实让 C 语言编码变得更容易、更有趣。和
C 的伟大之处在于它如此简单,而且仍然有足够的能力使这一切成为可能。

Wow, everybody is such a pessimist around here. The answer is yes.

It ain't trivial: by the end, we'll have the core function, a supporting struct, a wrapper function, and a macro
around the wrapper function. In my work I have a set of macros to automate all this; once
you understand the flow it'll be easy for you to do the same.

I've written this up elsewhere, so here's a detailed external link to supplement the summary here: http://modelingwithdata.org/arch/00000022.htm

We'd like to turn

double f(int i, double x)

into a function that takes defaults (i=8, x=3.14). Define a companion struct:

typedef struct {
    int i;
    double x;
} f_args;

Rename your function f_base, and define a wrapper function that sets defaults and calls
the base:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

Now add a macro, using C's variadic macros. This way users don't have to know they're
actually populating a f_args struct and think they're doing the usual:

#define f(...) var_f((f_args){__VA_ARGS__});

OK, now all of the following would work:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

Check the rules on how compound initializers set defaults for the exact rules.

One thing that won't work: f(0), because we can't distinguish between a missing value and
zero. In my experience, this is something to watch out for, but can be taken care of as
the need arises---half the time your default really is zero.

I went through the trouble of writing this up because I think named arguments and defaults
really do make coding in C easier and even more fun. And
C is awesome for being so simple and still having enough there to make all this possible.

半世蒼涼 2024-08-12 21:51:39

是的。 :-) 但不是以您期望的方式。

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

不幸的是,C 不允许您重载方法,因此您最终会得到两个不同的函数。不过,通过调用 f2,您实际上会使用默认值调用 f1。这是一个“不要重复自己”的解决方案,它可以帮助您避免复制/粘贴现有​​代码。

Yes. :-) But not in a way you would expect.

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

Unfortunately, C doesn't allow you to overload methods so you'd end up with two different functions. Still, by calling f2, you'd actually be calling f1 with a default value. This is a "Don't Repeat Yourself" solution, which helps you to avoid copying/pasting existing code.

北方。的韩爷 2024-08-12 21:51:39

并不真地。唯一的方法是编写一个可变参数函数并手动填写参数的默认值呼叫者没有通过。

Not really. The only way would be to write a varargs function and manually fill in default values for arguments which the caller doesn't pass.

三生殊途 2024-08-12 21:51:39

我们可以创建(仅)使用命名参数作为默认值的函数。这是 bk. 答案的延续。

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

C99 标准定义初始化中后面的名称会覆盖前面的项目。我们也可以有一些标准的位置参数,只需相应地更改宏和函数签名即可。默认值参数只能用于命名参数样式。

程序输出:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9

We can create functions which use named parameters (only) for default values. This is a continuation of bk.'s answer.

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

The C99 standard defines that later names in the initialization override previous items. We can also have some standard positional parameters as well, just change the macro and function signature accordingly. The default value parameters can only be used in named parameter style.

Program output:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9
ゃ人海孤独症 2024-08-12 21:51:39

OpenCV 使用类似以下内容:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

如果用户不知道应该写什么,这个技巧会很有帮助:

用法示例

OpenCV uses something like:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

If the user doesn't know what he should write, this trick can be helpful:

usage example

后eg是否自 2024-08-12 21:51:39

不。

即使是最新的 C99 标准也不支持这一点。

No.

Not even the very latest C99 standard supports this.

萌︼了一个春 2024-08-12 21:51:39

不,这是 C++ 语言的功能。

No, that's a C++ language feature.

能否归途做我良人 2024-08-12 21:51:39

也许最好的方法是转向 C++ 并将其用作“更好的 C”(根据您的具体情况,这可能会也可能不会)。您可以在不使用类、模板、运算符重载或其他高级功能的情况下使用 C++。

这将为您提供带有函数重载和默认参数(以及您选择使用的任何其他功能)的 C 变体。如果您真的很认真地只使用 C++ 的有限子集,那么您只需要遵守一点纪律即可。

很多人会说以这种方式使用 C++ 是一个糟糕的主意,他们可能有道理。但这只是一个意见;我认为使用您熟悉的 C++ 功能是有效的,而不必购买整个功能。我认为 C++ 成功的一个重要原因是它在早期就被大量程序员以这种方式使用。

Probably the best way to do this (which may or may not be possible in your case depending on your situation) is to move to C++ and use it as 'a better C'. You can use C++ without using classes, templates, operator overloading or other advanced features.

This will give you a variant of C with function overloading and default parameters (and whatever other features you chose to use). You just have to be a little disciplined if you're really serious about using only a restricted subset of C++.

A lot of people will say it's a terrible idea to use C++ in this way, and they might have a point. But's it's just an opinion; I think it's valid to use features of C++ that you're comfortable with without having to buy into the whole thing. I think a significant part of the reason for the sucess of C++ is that it got used by an awful lot of programmers in it's early days in exactly this way.

百合的盛世恋 2024-08-12 21:51:39

简短回答:不。

稍长回答:有一个古老的解决方法,您可以传递一个您解析的字符串 em> 用于可选参数:

int f(int arg1, double arg2, char* name, char *opt);

其中 opt 可能包含“名称=值”对或其他内容,您可以这样称呼

n = f(2,3.0,"foo","plot=yes save=no");

它显然这只是偶尔有用。通常当您需要一个接口来实现一系列功能时。


您仍然可以在由 C++ 专业程序编写的粒子物理代码中找到这种方法(例如 ROOT)。它的主要优点是它可以几乎无限期地扩展,同时保持向后兼容性。

Short answer: No.

Slightly longer answer: There is an old, old workaround where you pass a string that you parse for optional arguments:

int f(int arg1, double arg2, char* name, char *opt);

where opt may include "name=value" pair or something, and which you would call like

n = f(2,3.0,"foo","plot=yes save=no");

Obviously this is only occasionally useful. Generally when you want a single interface to a family of functionality.


You still find this approach in particle physics codes that are written by professional programs in c++ (like for instance ROOT). It's main advantage is that it may be extended almost indefinitely while maintaining back compatibility.

空气里的味道 2024-08-12 21:51:39

使用宏的另一个技巧:

#include <stdio.h>

#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)

int (func)(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", func(1));
    printf("%d\n", func(1, 2));
    return 0;
}

如果仅传递一个参数,则 b 接收默认值(在本例中为 15)

Another trick using macros:

#include <stdio.h>

#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)

int (func)(int a, int b)
{
    return a + b;
}

int main(void)
{
    printf("%d\n", func(1));
    printf("%d\n", func(1, 2));
    return 0;
}

If only one argument is passed, b receives the default value (in this case 15)

季末如歌 2024-08-12 21:51:39

还有一个选项使用struct

struct func_opts {
  int    arg1;
  char * arg2;
  int    arg3;
};

void func(int arg, struct func_opts *opts)
{
    int arg1 = 0, arg3 = 0;
    char *arg2 = "Default";
    if(opts)
      {
        if(opts->arg1)
            arg1 = opts->arg1;
        if(opts->arg2)
            arg2 = opts->arg2;
        if(opts->arg3)
            arg3 = opts->arg3;
      }
    // do stuff
}

// call with defaults
func(3, NULL);

// also call with defaults
struct func_opts opts = {0};
func(3, &opts);

// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);

Yet another option uses structs:

struct func_opts {
  int    arg1;
  char * arg2;
  int    arg3;
};

void func(int arg, struct func_opts *opts)
{
    int arg1 = 0, arg3 = 0;
    char *arg2 = "Default";
    if(opts)
      {
        if(opts->arg1)
            arg1 = opts->arg1;
        if(opts->arg2)
            arg2 = opts->arg2;
        if(opts->arg3)
            arg3 = opts->arg3;
      }
    // do stuff
}

// call with defaults
func(3, NULL);

// also call with defaults
struct func_opts opts = {0};
func(3, &opts);

// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);
远山浅 2024-08-12 21:51:39

不。

No.

留一抹残留的笑 2024-08-12 21:51:39

不,但您可以考虑使用一组函数(或宏)来近似使用默认参数:

// No default args
int foo3(int a, int b, int c)
{
    return ...;
}

// Default 3rd arg
int foo2(int a, int b)
{
    return foo3(a, b, 0);  // default c
}

// Default 2nd and 3rd args
int foo1(int a)
{
    return foo3(a, 1, 0);  // default b and c
}

No, but you might consider using a set of functions (or macros) to approximate using default args:

// No default args
int foo3(int a, int b, int c)
{
    return ...;
}

// Default 3rd arg
int foo2(int a, int b)
{
    return foo3(a, b, 0);  // default c
}

// Default 2nd and 3rd args
int foo1(int a)
{
    return foo3(a, 1, 0);  // default b and c
}
心如狂蝶 2024-08-12 21:51:39

是的,利用 C99 的功能,您可以做到这一点。这无需定义新的数据结构等即可工作,并且函数无需在运行时决定如何调用它,并且
没有任何计算开销。

有关详细说明,请参阅我的帖子

http://gustedt。 wordpress.com/2010/06/03/default-arguments-for-c99/

詹斯

Yes, with features of C99 you may do this. This works without defining new data structures or so and without the function having to decide at runtime how it was called, and
without any computational overhead.

For a detailed explanation see my post at

http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/

Jens

痞味浪人 2024-08-12 21:51:39

我改进了 Jens Gustedt 的答案,以便:

  1. 不使用内联函数,
  2. 默认值是在预处理
  3. 模块 期间计算的可重用的宏
  4. 可以设置编译器错误,该错误与允许的默认值的参数不足的情况有意义地匹配
  5. 如果参数类型保持明确,则不需要默认值来形成参数列表的尾部
  6. 与 C11 _Generic 互通
  7. 通过改变函数名称参数数量!

variadic.h:

#ifndef VARIADIC

#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)

// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)

#endif

简化的使用场景:

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
*/

#include "variadic.h"

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

并与_Generic:

const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);

const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);

const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/

#include "variadic.h"

#define   uint16_tobytes_2(a,    c) a, NULL, c
#define   uint16_tobytes_3(a, b, c) a,    b, c
#define   uint16_tobytes(...) VARIADIC(  uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint16_frombytes_2(   b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c)    a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   uint32_tobytes_2(a,    c) a, NULL, c
#define   uint32_tobytes_3(a, b, c) a,    b, c
#define   uint32_tobytes(...) VARIADIC(  uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   tobytes(a, ...) _Generic((a),                                                                                                 \
                                   const uint16*: uint16_tobytes,                                                                       \
                                   const uint32*: uint32_tobytes)  (VARIADIC2(  uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

#define frombytes(a, ...) _Generic((a),                                                                                                 \
                                         uint16*: uint16_frombytes,                                                                     \
                                         uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

并与可变参数函数名称选择,不能与_Generic结合使用:

// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.

#define   merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define   winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define   winternitz_5_name() merkle_lamport
#define   winternitz_7_name() winternitz
#define   winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)

I improved Jens Gustedt’s answer so that:

  1. inline functions aren’t employed
  2. defaults are computed during preprocessing
  3. modular reuseable macros
  4. possible to set compiler error that meaningfully matches the case of insufficient arguments for the allowed defaults
  5. the defaults aren’t required to form the tail of the parameter list if the argument types will remain unambiguous
  6. interopts with C11 _Generic
  7. vary the function name by the number of arguments!

variadic.h:

#ifndef VARIADIC

#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)

// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)

#endif

Simplified usage scenario:

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
*/

#include "variadic.h"

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

And with _Generic:

const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);

const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);

const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);

const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);

/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/

#include "variadic.h"

#define   uint16_tobytes_2(a,    c) a, NULL, c
#define   uint16_tobytes_3(a, b, c) a,    b, c
#define   uint16_tobytes(...) VARIADIC(  uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint16_frombytes_2(   b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c)    a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   uint32_tobytes_2(a,    c) a, NULL, c
#define   uint32_tobytes_3(a, b, c) a,    b, c
#define   uint32_tobytes(...) VARIADIC(  uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define uint32_frombytes_2(   b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c)    a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)

#define   tobytes(a, ...) _Generic((a),                                                                                                 \
                                   const uint16*: uint16_tobytes,                                                                       \
                                   const uint32*: uint32_tobytes)  (VARIADIC2(  uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

#define frombytes(a, ...) _Generic((a),                                                                                                 \
                                         uint16*: uint16_frombytes,                                                                     \
                                         uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))

And with variadic function name selection, which can't be combined with _Generic:

// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.

#define   merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define   winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define   winternitz_5_name() merkle_lamport
#define   winternitz_7_name() winternitz
#define   winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)
痴梦一场 2024-08-12 21:51:39

通常不需要,但在 gcc 中,您可以使用宏将 funcA() 的最后一个参数设置为可选。

在 funcB() 中,我使用特殊值 (-1) 来表示我需要“b”参数的默认值。

#include <stdio.h> 

int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) 


int funcB( int a, int b ){
  if( b == -1 ) b = 8;
  return a+b;
}

int main(void){
  printf("funcA(1,2): %i\n", funcA(1,2) );
  printf("funcA(1):   %i\n", funcA(1)   );

  printf("funcB(1, 2): %i\n", funcB(1, 2) );
  printf("funcB(1,-1): %i\n", funcB(1,-1) );
}

Generally no, but in gcc You may make the last parameter of funcA() optional with a macro.

In funcB() i use a special value (-1) to signal that i need the default value for the 'b' parameter.

#include <stdio.h> 

int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 ) 


int funcB( int a, int b ){
  if( b == -1 ) b = 8;
  return a+b;
}

int main(void){
  printf("funcA(1,2): %i\n", funcA(1,2) );
  printf("funcA(1):   %i\n", funcA(1)   );

  printf("funcB(1, 2): %i\n", funcB(1, 2) );
  printf("funcB(1,-1): %i\n", funcB(1,-1) );
}
提笔书几行 2024-08-12 21:51:39

通过宏

3个参数:

#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func3(char a, int b, float c) // b=10, c=0.5
{
    printf("a=%c; b=%d; c=%f\n", a, b, c);
}

如果你想要第四个参数,那么需要添加一个额外的my_func3。注意 VAR_FUNC、my_func2 和 my_func 中的变化

4 参数:

#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
    printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}

唯一的例外是 float 变量不能被赋予默认值(除非它是最后一个参数,如3 个参数 case),因为它们需要句点 ('.'),而宏参数中不接受句点。但可以找出解决方法,如 my_func2 宏(4 个参数情况

程序

int main(void)
{
    my_func('a');
    my_func('b', 20);
    my_func('c', 200, 10.5);
    my_func('d', 2000, 100.5, "hello");

    return 0;
}

输出所示:

a=a; b=10; c=0.500000; d=default                                                                                                                                                  
a=b; b=20; c=0.500000; d=default                                                                                                                                                  
a=c; b=200; c=10.500000; d=default                                                                                                                                                
a=d; b=2000; c=100.500000; d=hello  

YES

Through macros

3 Parameters:

#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func3(char a, int b, float c) // b=10, c=0.5
{
    printf("a=%c; b=%d; c=%f\n", a, b, c);
}

If you want 4th argument, then an extra my_func3 needs to be added. Notice the changes in VAR_FUNC, my_func2 and my_func

4 Parameters:

#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)

void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
    printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}

Only exception that float variables cannot be given default values (unless if it is the last argument as in the 3 parameters case), because they need period ('.'), which is not accepted within macro arguments. But can figure out a work around as seen in my_func2 macro (of 4 parameters case)

Program

int main(void)
{
    my_func('a');
    my_func('b', 20);
    my_func('c', 200, 10.5);
    my_func('d', 2000, 100.5, "hello");

    return 0;
}

Output:

a=a; b=10; c=0.500000; d=default                                                                                                                                                  
a=b; b=20; c=0.500000; d=default                                                                                                                                                  
a=c; b=200; c=10.500000; d=default                                                                                                                                                
a=d; b=2000; c=100.500000; d=hello  
攒一口袋星星 2024-08-12 21:51:39

我偶尔会使用一个技巧,它从 C99 开始就可用,它使用可变参数宏、复合文字和指定的初始值设定项。与任何宏解决方案一样,它很麻烦,通常不推荐,除非作为最后的手段...

我的方法是按以下方式构建的:

  • 将实际函数包装在类似函数的可变宏中:

    void myfunc(int x, int y) // 实际函数
    #define myfunc(...) myfunc(params) // 包装宏
    
  • 通过使用复合文字,将传递的参数复制到临时对象中。该对象应该是直接对应于函数的预期参数列表的私有结构。示例:

    typedef 结构
    {
      整数x;
      整数y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){__VA_ARGS__}
    

    这意味着初始化此结构时也使用将参数传递给函数时使用的相同类型安全(“按照分配”)规则。我们不会失去任何类型安全。同样,这会自动防止提供太多参数。

  • 但是,上面的内容不包括空参数列表的情况。为了解决这个问题,请添加一个虚拟参数,以便初始化列表永远不为空:

    typedef 结构
    {
      int 虚拟;
      整数x;
      整数y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
    
  • 类似地,我们可以计算传递的参数数量,假设传递的每个参数都可以隐式转换为 int

    #define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)

  • 我们为默认参数定义一个宏#define DEFAULT_ARGS (myfunc_t){0,1,2},其中 0 是虚拟值和 1,2 是默认值。

  • 将所有这些包装在一起,最外面的包装宏可能如下所示:

    #define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

    假设内部宏 MYFUNC_INIT 返回一个 myfunc_t 结构。

  • 内部宏根据参数列表的大小有条件地选择结构初始值设定项。如果参数列表很短,它将用默认参数填充。

    #define MYFUNC_INIT(...) \
      (myfunc_t){ 0, \
                  .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
                  .y = COUNT_ARGS(__VA_ARGS__)<2 ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
                }
    

完整示例:

#include <stdio.h>

void myfunc (int x, int y)
{
  printf("x:%d y:%d\n", x, y);
}

typedef struct
{
  int dummy;
  int x;
  int y;
} myfunc_t;

#define DEFAULT_ARGS (myfunc_t){0,1,2}
#define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
#define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)
#define MYFUNC_INIT(...) \
  (myfunc_t){ 0,         \
              .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
              .y = COUNT_ARGS(__VA_ARGS__)<2  ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
            }

#define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

int main (void)
{
  myfunc(3,4);
  myfunc(3);
  myfunc();
}

输出:

x:3 y:4
x:3 y:2
x:1 y:2

Godbolt: https://godbolt.org/z/4ns1zPW16 如您所见从 -O3 反汇编来看,复合文字的开销为零。


我注意到我的方法有点让人想起当前投票最高的答案。与其他解决方案进行比较:

优点:

  • 纯粹、可移植的标准 ISO C、没有肮脏的 gcc 扩展、没有定义不明确的行为。
  • 可以处理空参数列表。
  • 高效、零开销,不依赖于函数内联按预期执行。
  • 调用方没有晦涩的指定初始值设定项。

缺点:

  • 依赖于每个参数都可以隐式转换为 int,但情况通常并非如此。例如,严格的 C 不允许从指针到 int 的隐式转换 - 这种隐式转换是不符合标准(但流行)的编译器扩展。
  • 必须为每个函数生成默认参数和结构。虽然这个答案没有涵盖,但这可以通过 X 宏自动化。但这样做也会进一步降低可读性。

There's a trick I've occasionally used, which has been available since C99, using variadic macros, compound literals and designated initializers. As with any macro solution, it is cumbersome and generally not recommended other than as a last resort...

My method is built in the following way:

  • Wrap the actual function in a function-like, variadic macro:

    void myfunc (int x, int y)         // actual function
    #define myfunc(...) myfunc(params) // wrapper macro
    
  • By using compound literals, copy down the parameters passed into a temporary object. This object should be a private struct corresponding directly to the function's expected parameter list. Example:

    typedef struct
    {
      int x;
      int y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){__VA_ARGS__}
    

    This means that the same type safety ("as per assignment") rules used when passing parameters to a function is also used when initializing this struct. We don't lose any type safety. Similarly, this automatically guards against providing too many arguments.

  • However, the above doesn't cover the case of an empty argument list. To counter this, add a dummy argument so that the initializer list is never empty:

    typedef struct
    {
      int dummy;
      int x;
      int y;
    } myfunc_t;
    
    #define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
    
  • Similarly, we can count the number of arguments passed, assuming that every parameter passed can get implicitly converted to int:

    #define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)

  • We define a macro for the default arguments #define DEFAULT_ARGS (myfunc_t){0,1,2}, where 0 is the dummy and 1,2 are the default ones.

  • Wrapping all of this together, the outermost wrapper macro may look like:

    #define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

    This assuming that the inner macro MYFUNC_INIT returns a myfunc_t struct.

  • The inner macro conditionally picks struct initializers based on the size of the argument list. In case the argument list is short, it fills up with default arguments.

    #define MYFUNC_INIT(...) \
      (myfunc_t){ 0,         \
                  .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
                  .y = COUNT_ARGS(__VA_ARGS__)<2  ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
                }
    

Full example:

#include <stdio.h>

void myfunc (int x, int y)
{
  printf("x:%d y:%d\n", x, y);
}

typedef struct
{
  int dummy;
  int x;
  int y;
} myfunc_t;

#define DEFAULT_ARGS (myfunc_t){0,1,2}
#define PASSED_ARGS(...) (myfunc_t){0,__VA_ARGS__}
#define COUNT_ARGS(...) (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1)
#define MYFUNC_INIT(...) \
  (myfunc_t){ 0,         \
              .x = COUNT_ARGS(__VA_ARGS__)==0 ? DEFAULT_ARGS.x : PASSED_ARGS(__VA_ARGS__).x, \
              .y = COUNT_ARGS(__VA_ARGS__)<2  ? DEFAULT_ARGS.y : PASSED_ARGS(__VA_ARGS__).y, \
            }

#define myfunc(...) myfunc( MYFUNC_INIT(__VA_ARGS__).x, MYFUNC_INIT(__VA_ARGS__).y )

int main (void)
{
  myfunc(3,4);
  myfunc(3);
  myfunc();
}

Output:

x:3 y:4
x:3 y:2
x:1 y:2

Godbolt: https://godbolt.org/z/4ns1zPW16 As you can see from the -O3 disassembly, there is zero overhead from the compound literals.


I noticed that my method reminds a bit of the current, top-voted answer. For comparison with other solutions here:

Pros:

  • Pure, portable standard ISO C, no dirty gcc extensions, no poorly-defined behavior.
  • Can handle empty argument lists.
  • Efficient, zero overhead, doesn't rely on function inlining getting carried out as expected.
  • No obscure designated initializers on the caller-side.

Cons:

  • Relies on every parameter being implicitly convertible to int, which often isn't the case. For example strict C does not allow implicit conversions from pointers to int - such implicit conversions is a non-conforming (but popular) compiler extension.
  • Default args and structs have to be generated per function. Although not covered by this answer, this could be automated with X macros. But doing so will also reduce readability even further.
听风吹 2024-08-12 21:51:39

是的,你可以做一些模拟的事情,在这里你必须知道你可以获得的不同参数列表,但你有相同的函数来处理所有这些。

typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;

typedef struct{
    INPUT_SET type;
    char* text;
} input_set1;

typedef struct{
    INPUT_SET type;
    char* text;
    int var;
} input_set2;

typedef struct{
    INPUT_SET type;
    int text;
} input_set3;

typedef union
{
    INPUT_SET type;
    input_set1 set1;
    input_set2 set2;
    input_set3 set3;
} MY_INPUT;

void my_func(MY_INPUT input)
{
    switch(input.type)
    {
        case my_input_set1:
        break;
        case my_input_set2:
        break;
        case my_input_set3:
        break;
        default:
        // unknown input
        break;
    }
}

Yes you can do somthing simulair, here you have to know the different argument lists you can get but you have the same function to handle then all.

typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;

typedef struct{
    INPUT_SET type;
    char* text;
} input_set1;

typedef struct{
    INPUT_SET type;
    char* text;
    int var;
} input_set2;

typedef struct{
    INPUT_SET type;
    int text;
} input_set3;

typedef union
{
    INPUT_SET type;
    input_set1 set1;
    input_set2 set2;
    input_set3 set3;
} MY_INPUT;

void my_func(MY_INPUT input)
{
    switch(input.type)
    {
        case my_input_set1:
        break;
        case my_input_set2:
        break;
        case my_input_set3:
        break;
        default:
        // unknown input
        break;
    }
}
粉红×色少女 2024-08-12 21:51:39

您不需要仅将 VARARGS 与 C 一起使用。这是一个示例。

int funcA_12(int a1, int a2) { ... }

#define funcA(a1) funcA_12(a1, 0)

这个答案与上面的两个函数方法非常相似,但在这种情况下,您使用宏作为定义参数的函数名称。

you don't need to use VARARGS with just C. Here is an example.

int funcA_12(int a1, int a2) { ... }

#define funcA(a1) funcA_12(a1, 0)

This answer is very similar to the two functions method above but in this case, you're using a macro for the function name that defines the arguments.

半窗疏影 2024-08-12 21:51:39

https://github.com/cindRoberta/C/blob/主控/结构/函数/default_parameter.c

#include<stdio.h>

void f_impl(int a, float b) {
  printf("%d %g\n", a, b);
}

#define f_impl(...) f_macro(__VA_ARGS__, 3.7)
#define f_macro(a, b, ...) f_impl(a, b)

int main(void) {
  f_impl(1);
  f_impl(1, 2, 3, 4);

  return 0;
}

https://github.com/cindRoberta/C/blob/master/structure/function/default_parameter.c

#include<stdio.h>

void f_impl(int a, float b) {
  printf("%d %g\n", a, b);
}

#define f_impl(...) f_macro(__VA_ARGS__, 3.7)
#define f_macro(a, b, ...) f_impl(a, b)

int main(void) {
  f_impl(1);
  f_impl(1, 2, 3, 4);

  return 0;
}
温柔女人霸气范 2024-08-12 21:51:39

我知道如何以更好的方式做到这一点。
您只需将 NULL 分配给参数,因此,您将没有任何值。然后检查参数值是否为NULL,将其更改为默认值。

void func(int x){
if(x == NULL)
  x = 2;
....
}

不过,它会引起警告。更好的选择是分配一个值,如果参数值是这样的话,则该值不会执行任何操作:

void func(int x){
if(x == 1)
  x = 2;
....
}

在上面的示例中,如果 x1 函数会将其更改为 2;

感谢@user904963,编辑:
如果您必须涵盖所有数字范围,那么添加另一个参数只是为了告诉函数是否将参数设置为默认值并不难。

void func(int x, bool useDefault){
if(useDefault) //useDefault == true
  x = 2;
....
}

但是,请记住包含 stdbool.h

I know how to do this in a better manner.
You simply assign NULL to a parameter, so, you will have no value. Then you check if the parameter value is NULL, you change it to the default value.

void func(int x){
if(x == NULL)
  x = 2;
....
}

Though, it will cause warnings. a better choice would be to assign a value that will do nothing if the parameter value is that:

void func(int x){
if(x == 1)
  x = 2;
....
}

In the example above, if x is 1 the function changes it to 2;

Thanks to @user904963, EDIT:
if you have to cover all ranges of numbers, it's not hard to add another argument only to say to the function whether it would set the parameter to default or not

void func(int x, bool useDefault){
if(useDefault) //useDefault == true
  x = 2;
....
}

However, remember to include stdbool.h

风铃鹿 2024-08-12 21:51:39

我用bk适当地做了它。和埃里克。

#include <stdio.h>

// 구조체 생성
typedef struct range {
  int a;
} f_args;

// 매개변수 기본값 설정
#define func(...) func((f_args){.a = 20, __VA_ARGS__})
f_args(func)(f_args x);

int main(void) {
  // 매개변수 기본값 설정(선택 1)
  f_args r = func();

  // 매개변수 값 변환(선택 2)
  // f_args r = func(.a = 10);

  printf("%d ", r.a); //확인

  return 0;
}

// 매개변수 기본값 구성 가능한 함수
f_args(func)(f_args x) {
  printf("%d ", x.a); //확인
  x.a = x.a + 10;

  return x;
}

必须在#define(可变参数宏)中更改默认值

I made it appropriately by bk. and Eric.

#include <stdio.h>

// 구조체 생성
typedef struct range {
  int a;
} f_args;

// 매개변수 기본값 설정
#define func(...) func((f_args){.a = 20, __VA_ARGS__})
f_args(func)(f_args x);

int main(void) {
  // 매개변수 기본값 설정(선택 1)
  f_args r = func();

  // 매개변수 값 변환(선택 2)
  // f_args r = func(.a = 10);

  printf("%d ", r.a); //확인

  return 0;
}

// 매개변수 기본값 구성 가능한 함수
f_args(func)(f_args x) {
  printf("%d ", x.a); //확인
  x.a = x.a + 10;

  return x;
}

The default value must be changed in #define (variadic macros)

守护在此方 2024-08-12 21:51:39

为什么我们不能这样做。

为可选参数指定默认值。这样,函数的调用者不一定需要传递参数的值。该参数采用默认值。
并且很容易,这个论点对于客户来说就变成了可选的。

例如

void foo(int a, int b = 0);

这里 b 是一个可选参数。

Why can't we do this.

Give the optional argument a default value. In that way, the caller of the function don't necessarily need to pass the value of the argument. The argument takes the default value.
And easily that argument becomes optional for the client.

For e.g.

void foo(int a, int b = 0);

Here b is an optional argument.

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