C默认参数

有没有一种方法来指定C中的函数的默认参数?

不是真的。 唯一的方法是编写一个可变参数函数,并手动填充调用者不传递参数的默认值。

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

这不是微不足道的:最后,我们将有包装函数的核心function,支持结构,包装函数和macros。 在我的工作中,我有一套macros来自动化所有这些; 一旦你了解了stream程,你也很容易做到这一点。

我已经在其他地方写了这个,所以这里有一个详细的外部链接来补充总结: 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的可变参数macros来添加一个macros。 这样用户不必知道他们实际上是在填充一个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很简单,而且还有足够的空间让所有这些成为可能。

是。 :-)但不是你所期望的。

 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。 这是一个“不要重复自己”的解决scheme,它可以帮助您避免复制/粘贴现有​​的代码。

我们可以创build使用命名参数(仅)默认值的函数。 这是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标准定义了以后的名字在初始化中覆盖了以前的项目。 我们也可以有一些标准的位置参数,只是相应的改变macros和函数签名。 默认值参数只能用于命名参数样式。

节目输出:

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

没有。

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

不,这是一个C ++语言function。

另一个选项使用struct s:

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

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

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

用法示例

也许最好的方法来做到这一点(根据您的情况可能或不可能)是移到C ++并将其用作“更好的C”。 您可以使用C ++,而无需使用类,模板,运算符重载或其他高级function。

这会给你一个带有函数重载和默认参数(以及你select使用的其他特性)的变体。 如果你真的只想使用C ++的一个有限子集,那么你只需要小心一点。

很多人会这样说使用C ++是一个糟糕的主意,他们可能有一个观点。 但是这只是一个意见。 我认为使用C ++的特性是合理的,而不必购买整个产品。 我认为C ++成功的一个重要原因就是它在很早以前被很多程序员使用。

简短的回答:不。

稍微长一点的答案:有一个旧的, 旧的解决方法,你传递一个string,你parsing的可选参数:

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

在哪里select可能包括“名称=价值”双或什么,你会打电话给

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

显然这只是偶尔有用的。 一般来说,当你想要一个function家族的单一接口。


您仍然可以在由c ++专业程序编写的粒子物理代码中find这种方法(例如ROOT )。 它的主要优点是它可以在保持后向兼容性的情况下几乎无限延伸。

没有。

不,但是您可以考虑使用一函数(或macros)来使用默认参数进行近似:

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

是的,用C99的function,你可以做到这一点。 这个工作不需要定义新的数据结构,也不需要在运行时决定它是如何被调用的,而且没有任何计算开销。

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

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

一般不行,但在gcc中你可以用macros来设置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) ); } 

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

  1. 内联函数不被使用
  2. 在预处理期间计算默认值
  3. 模块化可重用的macros
  4. 可能设置的编译器错误,有意义的匹配的情况下允许的默认值的参数不足
  5. 如果参数types保持明确,则缺省值不需要形成参数列表的尾部
  6. 与C11 _Generic的interopts
  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__)) 

并用可变参数函数名称select,它不能与_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__) 

是的,你可以做任何simulair,在这里你必须知道你可以得到不同的参数列表,但你有相同的function来处理所有。

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

为什么我们不能这样做。

给可选参数一个默认值。 这样,函数的调用者不一定需要传递参数的值。 该参数采用默认值。 而且这个参数很容易成为客户端的可选项。

例如

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

这里b是一个可选的参数。