STDARG(3)翻譯

名字

stdarg, va_start, va_arg, va_end, va_copy可變參數列表。

概要

#include <stdarg.h>

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

描述

一個函數可能有不同數量的不同類型的參數。頭文件<stdarg.h>聲明瞭一個va_list類型,並且定義3個宏來處理被調函數的參數列表。被調函數必須聲明一個被va_start, va_arg和va_end使用的類型爲va_list的對象。

va_start()

va_start()宏初始化一個被後面va_arg()和va_end()使用的ap,並且必須率先調用。last是在可變參數列表之前的最後一個我們知道類型的參數的名字。因爲va_start()宏可能會用到last的地址,所以不要把last聲明成寄存器變量或者函數、數組類型。

va_arg()

va_arg()這個宏擴展到調用中的下一個參數的值和類型。ap就是由va_start()初始化的va_list類型的ap。每次調用va_arg(),它都會修改ap,使它的下一次調用返回下一個參數。參數type就是一個類型名,這樣就可以獲取一個類型對象了。

在調用va_start()之後第一次調用va_arg()它就返回last後面的第一個參數。後面的調用,依次返回剩下的參數的值。

如果後面沒有其它參數了,或者type和實際的下一個參數不匹配(會按照默認參數擴展?),就會產生隨機的錯誤。

如果ap在調用va_arg(ap, type)的時候是指向一個函數的,那麼在這個函數返回的時候ap的值是未定義的。

va_end()

每次在一個函數裏面調用va_start(),必須有一個va_end()與它進行匹配。用多對va_start()和va_end()多次遍歷一個va_list也是可以的。va_end()可以是一個宏或者一個函數。

va_copy()

va_copy()將之前初始化的可變參數列表從src拷貝到dest。這個行爲等價於先對dest做va_start(),然後調用獲得src相同次數的va_arg()。

一個顯而易見的實現是讓va_list指向可變函數的棧幀。這樣的話(目前最常見),這樣的話,賦值操作好像也沒什麼問題。

va_list aq = ap;

不幸的是,有些系統也把va_list實現成一個指針數組。那這樣的話就需要:

va_list aq;
*aq = *ap;

最後,對於參數傳遞到寄存器的系統,就有必要用va_start()分配內存,存儲參數,並且表明哪個參數是下一個。這樣的話va_arg()纔可以對參數進行遍歷。然後va_end()可以釋放內存。爲了解決這個問題,C99添加了一個宏va_copy()。那麼上面的賦值就可以用下面的函數代替:

va_list aq;
va_copy(aq, ap);
...
va_end(aq);

每次調用va_copy(),必須在同一個函數裏面調用對應的va_end()。有些系統不提供va_copy(),但是有__va_copy,這個是草案裏面使用的名字。

屬性

這部分使用的術語的解釋可以參看attribute(7)。

接口 屬性
va_start(), va_end(), va_copy() 線程安全性 MT-safe
va_arg() 線程安全性 MT-safe race:ap

規範

va_start(), va_arg(), va_end()宏遵守C89,va_copy()遵守C99。

註釋

這些宏和它們代替的歷史上的宏並不兼容。一個後向兼容的版本可以在頭文件<varargs.h>中找到。

歷史的背景是:

#include <varargs.h>

void
foo(va_alist)
    va_dcl
{
    va_list ap;

    va_start(ap);
    while (...) {
        ...
        x = va_arg(ap, type);
        ...
    }
    va_end(ap);
}

有些系統,va_end包含一個“}”,va_start包含一個“{”,這樣的話兩個宏就必須出現在同一個函數裏面,否則編譯就不會通過。

BUGS

不像varargs宏,stdarg宏不允許程序員編碼一個沒有固定參數的函數。這個問題主要是在將varargs代碼轉換爲stdarg代碼的時候引入了很多工作量。當然,這個也給從可變參數函數內部將它所有的參數傳遞給一個有一個va_list參數的函數(比如vfprintf(3))帶來了很多的困難。

例子

函數foo把一個格式字符串作爲參數,並且根據每個格式字符表明的類型打印出每個參數。

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

void
foo(char *fmt, ...)
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while(*fmt)
        switch(*fmt++) {
        case 's':              /* string */
            s = va_arg(ap, char *);
            printf("string %s\n", s);
            break;
        case 'd':              /* int */
            d = va_arg(ap, int);
            printf("int %d\n", d);
            break;
        case 'c':              /* char */
            /* Need a cast hear since va_arg only
               takes fully promoted types */
            c = (char) va_arg(ap, int);
            printf("char %c\n", c);
            break;
        }
    va_end(ap);
}

版權

本頁是Linux man-page項目4.15版的一部分。關於項目的描述,報bug相關信息,以及本頁的最新版本可以在https://www.kernel.org/doc/man-pages/找到。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章