题
有没有办法为 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__});
OK,现在所有的以下将工作:
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是真棒为是如此简单,仍然具有足够的存在,使这一切成为可能。
是的。:-) 但不是以您期望的方式。
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。这是一个“不要重复自己”的解决方案,它可以帮助您避免复制/粘贴现有代码。
我们可以创建使用命名参数(仅)为默认值的功能。这是的浅滩答案的延续。
#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
的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));
如果用户不知道他应该写什么,这一招可以帮助:
没有
甚至没有的最新C99标准支持这一点。
没有,这是一个C ++语言特性。
可能做到这一点(其可以是或在您的情况下根据情况可能不是可能的)是移动到C ++,并使用它作为“更合适的C”的最佳方式。可以使用C ++,而无需使用类,模板,操作符重载或其它先进的功能。
这会给你的C与函数重载和默认参数的变体(和任何其它的特征您选择使用)。你只需要,如果你即将仅使用C ++的限制的子认认真真的有点纪律。
这是很多人会说这是用这种方式来使用C ++一个可怕的想法,他们可能有一个点。但就是这只是一个意见;我认为这是合法使用的C ++特性,你感到满意,而无需购买到整个事情。我想对于C ++的研制成功原因的显著的部分是,它得到了使用非常多的程序员在它的初期恰好这种方式。
<强>短答案:否
稍长的答案:有一个古老的,,你传递一个字符串老的变通方法,您的解析的可选参数:
int f(int arg1, double arg2, char* name, char *opt);
其中选择可以包括“名称=值”对什么的,并且你将调用等
n = f(2,3.0,"foo","plot=yes save=no");
显然,这只是偶尔是有用的。一般来说,当你想要一个单一的接口一个家庭的功能。
您仍然发现在由专业的程序用C写粒子物理代码这种做法++(例如像 ROOT )。它的主要优点是,它几乎可以无限延长,同时保持背兼容性。
又一选项使用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);
没有。
没有,但你可能会考虑使用的设置的函数(或宏)使用默认参数传递给逼近的:
// 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的功能,你可以做到这一点。这个工作没有定义新的数据结构或因此而不必在运行时决定它如何被调用的函数, 无需任何计算开销。
有关的详细说明见我的帖子在
http://gustedt.wordpress.com/ 2010/06/03 /默认参数换C99 /
延
一般无,但在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) );
}
我改进了 Jens Gustedt 的 回答 以便:
- 未使用内联函数
- 默认值是在预处理期间计算的
- 模块化可重用宏
- 可以设置编译器错误,该错误与允许的默认值的参数不足的情况有意义地匹配
- 如果参数类型保持明确,则不需要默认值来形成参数列表的尾部
- 与 C11 _Generic 互操作
- 根据参数数量改变函数名称!
可变参数.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__)
使用宏另一个技巧:
#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)
是的,你可以做类似的事情,在这里你必须知道不同的参数列表,你可以得到,但你有相同的函数来处理它们。
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;
}
}
是的
通过宏
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);
}
唯一例外的是 漂浮 变量不能被赋予默认值(除非它是最后一个参数,如 3 个参数的情况),因为它们需要句点('.'),而宏参数中不接受句点('.')。但可以找出解决方法,如 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
为什么我们不能做到这一点。
提供可选的参数的缺省值。这样一来,该函数的调用者不一定需要通过参数的值。该参数采用默认值。 且容易地这样的说法成为可选的用于客户端。
有关e.g。
无效美孚(INT A,INT B = 0);
下面b是可选参数。