无参函数的应用与限制
在 C90 标准中,你可以声明一个无参数信息的函数。一个示例如下:
void no_arg_func();
使用空的小括号来表示没有参数信息,编译器无法获取到参数信息,也就不会进行参数检查,因此你可以传递任意数量的参数。下面是一个具体的应用。
#include <stdio.h>
#include <stdarg.h>
#define MAXARGC 2
#define TEST_BEGIN() \
printf("some variable initialized, or something else\n")
#define TEST_END() \
printf("some teardown procedure, or something else\n");
/* function prototype */
extern int init_data(const char *name, int id);
extern char *find_name(int id);
extern int find_id(const char *name);
extern int destroy_data(int id);
typedef void (*func_t)();
extern void execute_a_test(func_t text_func, int count, ...);
int main(int argc, char *argv[])
{
execute_a_test((func_t)init_data, 2, "test", -5);
execute_a_test((func_t)find_name, 1, -5);
execute_a_test((func_t)find_id, 1, "test");
execute_a_test((func_t)destroy_data, 1, -5);
return 0;
}
int init_data(const char *name, int id)
{
printf("name is %s, id is %d, %s\n", name, id, __func__);
}
char *find_name(int id)
{
printf("id is %d, %s\n", id, __func__);
}
int find_id(const char *name)
{
printf("name is %s, %s\n", name, __func__);
}
int destroy_data(int id)
{
printf("id is %d, %s\n", id, __func__);
}
void execute_a_test(func_t test_func, int count, ...)
{
long argument[MAXARGC];
int i = 0;
va_list ap;
if (test_func == NULL) {
return;
}
va_start(ap, count);
for (; i < count; i++) {
argument[i] = va_arg(ap, long);
}
va_end(ap);
TEST_BEGIN();
(*test_func)(argument[0], argument[1]);
TEST_END();
}
在上面的代码中,我需要测试四个函数,这四个函数的测试过程大同小异,它们最大的区别在于参数个数的不同。在这里我使用无参函数将四个待测试函数强转后作为参数传入到 execute_a_test 中,通过可变长参数统一不同参数,以最长的参数个数来调用函数,不区分指针与值,由于 test_func 没有参数信息,因此我们可以传递任意个数的参数,多余的参数子函数不会使用,不会造成问题。
execute_a_test 函数可以进行如下修改:
void execute_a_test(func_t test_func, int count, ...)
{
long argument[MAXARGC];
int i = 0;
va_list ap;
if (test_func == NULL) {
return;
}
va_start(ap, count);
for (; i < count; i++) {
argument[i] = va_arg(ap, long);
}
va_end(ap);
TEST_BEGIN();
if (count == 1) {
(*test_func)(argument[0]);
} else if (count == 2) {
(*test_func)(argument[0], argument[1]);
}
TEST_END();
}
这个修改避免了传递多余参数的问题,但是加入的条件分支会降低程序的性能。
无参函数是 c 语言的旧标准中支持的特性,新标准虽然能够兼容旧标准,但最好不要使用无参函数。这样我们对上述程序再次进行修改,改为不使用无参函数的实现。
#include <stdio.h>
#include <stdarg.h>
#define TEST_BEGIN() \
printf("some variable initialized, or something else\n")
#define TEST_END() \
printf("some teardown procedure, or something else\n");
/* function prototype */
extern int init_data(const char *name, int id);
extern char *find_name(int id);
extern int find_id(const char *name);
extern int destroy_data(int id);
typedef int (*func_one)(long);
typedef int (*func_two)(long, long);
extern void execute_a_test(func_two func, long arg1, long arg2);
extern void execute_one_arg_functest(func_one func, long arg);
extern void execute_two_arg_functest(func_two func, long arg1, long arg2);
int main(int argc, char *argv[])
{
execute_a_test((func_two)init_data, (long)"test", (long)-5);
execute_a_test((func_two)find_name, (long)-5, (long)0);
execute_a_test((func_two)find_id, (long)"test", (long)0);
execute_a_test((func_two)destroy_data, (long)-5, (long)0);
execute_two_arg_functest((func_two)init_data, (long)"test", (long)-5);
execute_one_arg_functest((func_one)find_name, (long)-5);
execute_one_arg_functest((func_one)find_id, (long)"test");
execute_one_arg_functest((func_one)destroy_data, (long)-5);
return 0;
}
int init_data(const char *name, int id)
{
printf("name is %s, id is %d, %s\n", name, id, __func__);
}
char *find_name(int id)
{
printf("id is %d, %s\n", id, __func__);
}
int find_id(const char *name)
{
printf("name is %s, %s\n", name, __func__);
}
int destroy_data(int id)
{
printf("id is %d, %s\n", id, __func__);
}
void execute_a_test(func_two func, long arg1, long arg2)
{
if (func == NULL) {
return;
}
TEST_BEGIN();
(*func)(arg1, arg2);
TEST_END();
}
void execute_one_arg_functest(func_one func, long arg)
{
if (func == NULL) {
return;
}
TEST_BEGIN();
(*func)(arg);
TEST_END();
}
void execute_two_arg_functest(func_two func, long arg1, long arg2)
{
if (func == NULL) {
return;
}
TEST_BEGIN();
(*func)(arg1, arg2);
TEST_END();
}
上面的代码中,使用了多次强转,这是一大不足。实际上可以将指针与值分开,编写单独的函数,但这样又会增强代码的耦合度。写来写去还是觉得使用可变长参数可能更好点!最终版函数如下:
void execute_a_test(func_t test_func, int count, ...)
{
long argument[MAXARGC];
int i = 0;
va_list ap;
if (test_func == NULL) {
return;
}
va_start(ap, count);
for (; i < count; i++) {
argument[i] = va_arg(ap, long);
}
va_end(ap);
TEST_BEGIN();
if (count == 1) {
(*(func_one)test_func)(argument[0]);
} else if (count == 2) {
(*(func_two)test_func)(argument[0], argument[1]);
}
TEST_END();
}