天天看點

C 語言的帶有預設參數函數和不定參數函數的一種實作

作者:程式設計黑科技

C 語言中的文法預設上是不支援函數的預設形參,通過__VA_ARGS__宏的機制包裝實作類似的機制(目前僅限于學習,業務不建議使用,意義不大)。

關于不定參數函數,C 語言預設支援是有的,比如 printf(), scanf() 等函數都是例子,但是它們都不支援 0 個參數。通過自己的思考,可通過__VA_ARGS__, #__VA_ARGS__, ##__VA_ARGS__ 宏的機制擴充支援 0 個參數的場景。

以上描述的情況的代碼示例如下:

// 檔案名:variable_para.c

#include <stdio.h>
#include <stdarg.h>

// 該方式計算函數參數個數,會有一個最大數限制,以下的版本是最多 64 個參數
// 如果要修改限制可以修改ARG_N_M及ARG_N_RESQ對應的參數及資料個數
#define ARG_N(...) ARG_N_(0, ##__VA_ARGS__, ARG_N_RESQ)

#define ARG_N_(...) ARG_N_M(__VA_ARGS__)

#define ARG_N_M(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10,      \
                _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
                _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
                _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
                _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
                _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
                _61, _62, _63, N, ...) N

#define ARG_N_RESQ 63, 62, 61, 60,                         \
                   59, 58, 57, 56, 55, 54, 53, 52, 51, 50, \
                   49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
                   39, 38, 37, 36, 35, 34, 33, 32, 31, 30, \
                   29, 28, 27, 26, 25, 24, 23, 22, 21, 20, \
                   19, 18, 17, 16, 15, 14, 13, 12, 11, 10, \
                   9, 8, 7, 6, 5, 4, 3, 2, 1, 0

#define INFO(...) myinfo(ARG_N(__VA_ARGS__), ##__VA_ARGS__)

// 目标函數
int info(int a, char *b, int c, int d)
{
    printf("a: %d b: %s c: %d d: %d\n", a, b, c, d);
}

// 适配函數, 通過傳入的參數的個數不同,來補充參數
#define ARG_1 0
#define ARG_2 "test"
#define ARG_3 2
#define ARG_4 3
int myinfo(int argc, ...)
{
    int a = ARG_1;
    char *b = ARG_2;
    int c = ARG_3;
    int d = ARG_4;
    va_list argptr;
    va_start(argptr, argc);
    if (argc >= 4)
    {
        // 如果提供過多參數,則自動忽略
        a = va_arg(argptr, int);
        b = va_arg(argptr, char *);
        c = va_arg(argptr, int);
        d = va_arg(argptr, int);
    }
    else if (argc == 3)
    {
        a = va_arg(argptr, int);
        b = va_arg(argptr, char *);
        c = va_arg(argptr, int);
    }
    else if (argc == 2)
    {
        a = va_arg(argptr, int);
        b = va_arg(argptr, char *);
    }
    else if (argc == 1)
    {
        a = va_arg(argptr, int);
    }
    va_end(argptr);
    return info(a, b, c, d);
}

#define DEFAULT_DEBUG "This is default debug (%s:%u)\n", __FILE__, __LINE__
#define DEBUG(...) #__VA_ARGS__[0] ? myprintf(#__VA_ARGS__, ##__VA_ARGS__) : myprintf(#__VA_ARGS__, DEFAULT_DEBUG)
int myprintf(const char *flag, ...)
{
    int cnt;
    va_list argptr;
    va_start(argptr, flag);
    char *format = va_arg(argptr, char *);
    cnt = vprintf(format, argptr);
    va_end(argptr);
    return cnt;
}

int main(int argc, char const *argv[])
{
    INFO();
    INFO(10);
    INFO(10, "info");
    INFO(10, "info", 8);
    INFO(10, "info", 8, 7);
    INFO(10, "info", 8, 7, 6);

    DEBUG();
    DEBUG("%d\n", ARG_N(1, 2));
    DEBUG("%d\n", 123);
    DEBUG("%d %s %f\n", 123, "test", 1.2);
    DEBUG("%d %s %f %x\n", 123, "test", 1.2, 0x1234);
    DEBUG("%d %s %f %x %c\n", 123, "test", 1.2, 0x1234, 78);
    DEBUG("%d %s %f %x %c %p\n", 123, "test", 1.2, 0x1234, 78, 56789);

    return 0;
}
           

編譯及執行指令如下:

gcc variable_para.c -o variable_para && ./variable_para            

執行結果如下:

a: 0 b: test c: 2 d: 3
a: 10 b: test c: 2 d: 3
a: 10 b: info c: 2 d: 3
a: 10 b: info c: 8 d: 3
a: 10 b: info c: 8 d: 7
a: 10 b: info c: 8 d: 7
This is default debug (variable_para.c:100)
2
123
123 test 1.200000
123 test 1.200000 1234
123 test 1.200000 1234 N
123 test 1.200000 1234 N 0xddd5           

繼續閱讀