有没有一种方法可以在C语言中指定函数的默认参数?


当前回答

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

其他回答

我改进了Jens Gustedt的回答:

不使用内联函数 默认值在预处理期间计算 模块化可重用宏 可以设置编译器错误,使其与允许的默认值参数不足的情况有意义地匹配 如果实参类型保持无二义性,则不需要默认值来形成形参列表的尾部 与C11 _Generic互操作 根据参数的数量来改变函数名!

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__)

是的,有了C99的功能,你就可以这样做了。这种工作方式不需要定义新的数据结构,也不需要函数在运行时决定如何调用 没有任何计算开销。

有关详细的解释,请参阅我的帖子

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

Jens

不,那是c++语言的特性。

我偶尔会使用一个技巧,它从C99开始就可用了,使用可变宏、复合字面量和指定初始化式。与任何宏解决方案一样,它很麻烦,通常不推荐使用,除非是最后的手段……

我的方法是这样构建的:

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, \ }


完整的例子:

#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宏实现自动化。但这样做也会进一步降低可读性。

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

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