有没有一种方法可以在C语言中指定函数的默认参数?
当前回答
做到这一点的最好方法(根据你的情况,这可能也可能不可能)可能是转移到c++,并将其作为“更好的C”使用。你可以在不使用类、模板、操作符重载或其他高级特性的情况下使用c++。
这将为您提供一个具有函数重载和默认参数(以及您选择使用的任何其他特性)的C变体。如果你真的想只使用c++的一个受限子集,你就必须稍微自律一些。
很多人会说以这种方式使用c++是一个糟糕的想法,他们可能是有道理的。但这只是一种观点;我认为使用c++中你觉得舒服的特性是合理的,而不必购买整个东西。我认为c++成功的一个重要原因是它在早期被大量的程序员以这种方式使用。
其他回答
我知道如何更好地做到这一点。 您只需将NULL赋值给一个参数,因此,您将没有值。然后检查参数值是否为NULL,将其更改为默认值。
void func(int x){
if(x == NULL)
x = 2;
....
}
尽管如此,它还是会引起警告。一个更好的选择是,如果参数值是:
void func(int x){
if(x == 1)
x = 2;
....
}
在上面的例子中,如果x是1,函数将其更改为2;
感谢@user904963,编辑: 如果必须覆盖所有的数字范围,那么添加另一个参数来告诉函数是否将形参设置为默认并不难
void func(int x, bool useDefault){
if(useDefault) //useDefault == true
x = 2;
....
}
但是,请记住包含stdboolh
我偶尔会使用一个技巧,它从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宏实现自动化。但这样做也会进一步降低可读性。
我们可以为默认值创建(仅)使用命名形参的函数。这是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
你不需要只在c上使用VARARGS,下面是一个例子。
int funcA_12(int a1, int a2) { ... }
#define funcA(a1) funcA_12(a1, 0)
这个答案与上面的两个函数方法非常相似,但在这种情况下,您使用的是定义参数的函数名宏。