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


当前回答

哇,这里的每个人都这么悲观。答案是肯定的。

这并不简单:到最后,我们将拥有核心函数、支持结构、包装器函数和宏 围绕包装器函数。在我的工作中,我有一组宏来自动化所有这些;一次 你了解流程,你也会很容易做到。

我已经在其他地方写过了,所以这里有一个详细的外部链接来补充这里的摘要: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语言太棒了,因为它如此简单,而且仍然有足够的内容使这一切成为可能。

其他回答

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

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

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

哇,这里的每个人都这么悲观。答案是肯定的。

这并不简单:到最后,我们将拥有核心函数、支持结构、包装器函数和宏 围绕包装器函数。在我的工作中,我有一组宏来自动化所有这些;一次 你了解流程,你也会很容易做到。

我已经在其他地方写过了,所以这里有一个详细的外部链接来补充这里的摘要: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语言太棒了,因为它如此简单,而且仍然有足够的内容使这一切成为可能。

我改进了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__)

一般不会,但在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) );
}