可變參數列表源碼的剖析

在某些情況下我們希望函數參數的個數可以根據實際需要來確定,所以C語言中就提供了一種長度不確定的參數,形如:“…”,通過將函數實現爲可變參數的形式,可以使得函數可以接受1個以上的任意多個參數。

典型的例子有printf()、scanf()函數等,下面就用printf函數的原型爲例分析:

int printf( const char *format [, argument]... );

如上,除了參數format固定以外,其他參數的類型和個數是不確定的。在實際調用的時候有如下類型:

printf("%d",num);
printf("%s",num);
printf("%c",num);
...

在標準C語言中定義了一個頭文件,專門用來對付可變參數列表,其中,包含了一個va_list的typedef聲明和一組宏定義va_start、va_arg、va_end,如下所示:

va_list arg;
va_start(arg, n);
va_arg(arg, (數據類型) );
va_end(arg);

va_list:聲明一個va_list類型的變量arg,它可以訪問參數列表的未確定部分。

這個變量是調用va_start來初始化的。它的第一個參數是va_list的變量名,第2個參數是省略號前最後一個有名字的參數。初始化過程把arg變量設置爲指向可變參數部分的第一個參數。

va_arg:這個宏和接受兩個參數,va_list變量和參數列表中下一個參數的類型。在這個例子中所有的可變參數都是整型。va_arg返回這個參數的值,並使用va_arg指向下一個可變參數。

va_end:訪問完畢最後一個可變參數,通過va_end(ap)讓ap不再指向堆棧。

例:自定義的打印函數

int my_printf(char *str, ...)
{
    va_list arg;
    char* str_tmp = NULL;
    char buf[10] = {0};
    va_start(arg, str);
    while (*str != '\0')
    {
        switch (*str)
        {
        case 's':
            str_tmp = (char*)va_arg(arg, int);//取下一個參數的地址,因爲這個是字符串
            while (*str_tmp != '\0')//利用解引用進行輸出
            {
                putchar(*str_tmp);
                str_tmp++;
            }
            break;
        case 'c':
            putchar(va_arg(arg, int));
            break;
        case 'd':
            int d = va_arg(arg, int);
            _itoa(d, buf, 10);
            for (str_tmp = buf; *str_tmp; str_tmp++) 
            {
                putchar(*str_tmp);
            }
            break;
        case '\n':
            puts(" ");
            break;
        default:
            ;
            break;
        }
        str++;
    }
    va_end(arg);
    return 0;
}

int main()
{
    my_printf("s ccc d.\n", "hello", 'z', 'z', 'z','520');
    system("pause");
    return 0;
}

由於將va_start、va_arg、va_end定義成了宏,可變參數的類型和個數在該函數中完全由程序代碼控制,並不能智能地進行識別,所以導致編譯器對可變參數的函數原型檢查不夠嚴格,難於查錯,不利於寫出高質量的代碼。
——《編寫高質量代碼》

雖然參數可變爲程序員帶來了很多便利,但也有一些不可避免的缺陷。比如:

1.缺乏類型檢查,類型安全性不能保證。

2.因爲禁用了語言類型檢查功能,所以在調用時必須通過其他方式告訴函數所傳遞參數的類型。

3.不支持自定義數據類型。

以上,因爲編譯器對可變參數函數的原型檢查不夠嚴格,所以容易引起問題,難於查錯,不利於寫出高質量的代碼。所以應當儘量避免使用C語言方式的可變參數設計。

發佈了35 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章