1. 程式人生 > >無參函式的應用與限制

無參函式的應用與限制

無參函式的應用與限制

在 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();
}