2010-02-05 20 views
6

這是一個奇怪的問題,但是在將它傳遞給另一個函數之前,是否有一種標準的方法來處理va_list的內容?舉例來說,假設我有兩個功能,sumvsum操縱可變參數的標準方法?

int vsum(int n, va_list ap) { 
    int total = 0; 
    for (int i = 0; i < n; ++i) { 
     total += va_arg(n, int); 
    return total; 
} 

int sum(int n, ...) { 
    va_list ap; 
    va_start(ap, n); 
    int total = vsum(n, ap); 
    va_end(ap); 
    return total; 
} 

如果我打電話sumsum(4, 1, 2, 3, 4),我希望得到的結果10.現在讓我們假設,而不是直接調用vsumsum調用中間函數,vsum_stub其執行以下操作:

int vsum_stub(int n, va_list ap) { 
    va_list temp_ap; 
    va_copy(temp_ap, ap); 
    for (int i = 0; i < n; ++i) { 
     int *arg = &va_arg(ap, int); 
     *arg += 2; 
    } 
    va_end(temp_ap); 
    return vsum(n, ap); 
} 

現在,當我打電話sum(4, 1, 2, 3, 4),我要取回結果20,因爲vsum_stub增量的所有值在va_list乘2.這當然不會編譯,因爲你不能把地址的結果作爲va_arg。還有另一種方法可以做到這一點嗎?我在C99工作。


背景:

我的工作,做一些翻譯的指針,這樣的數據可以存儲在堆在一個更有效的格式庫。程序使用自定義轉換進行編譯,該轉換將調用轉換爲庫函數(如printf),並將其轉換爲我自己的存根函數(例如,hc_printf)。 hc_printf需要在將參數傳遞給真實的printf函數之前翻譯任何指針參數(用於%s的字符串)。

編輯:下面是一個代碼示例。假設我們有一個字符串foofoo動態分配一個修改後的版本malloc,它返回一個假指針。編譯器修改程序,以便它可以處理假指針。所以這個工程:

char *foo = fake_malloc(4); 
fake_strcpy(foo, "foo"); 

我想寫一個fake_vprintf函數這樣的(僞代碼):

int fake_vprintf(const char *format, va_list args) { 
    for each pointer argument p in args 
     translate p to q, a real pointer to contiguous memory 
     replace p with q in args 
    } 
    return vprintf(format, args); 
} 

該計劃將使用假指針調用fake_vprintf就像當初vprintffake_vprintf將假指針轉換爲實際vprintf可以使用的實指針。

+0

不應該'int * arg =&va_arg(ap,int);'實際上是'int * arg = va_arg(ap,int *);'? – dirkgently 2010-02-05 21:06:04

+0

@dirkgently,不,參數列表中的實際參數是一個int。我在這個例子中的意圖是獲得一個指向該列表中的int的指針,以便我可以改變它的值。 – 2010-02-05 21:11:03

+0

我假設'vsum_stub'接受了一個'int *'列表。無論如何,你認爲我發佈的代碼有什麼幫助嗎? – dirkgently 2010-02-05 21:14:51

回答

2

啊哈,據我所知,您的問題是創建一個新的va_list參數傳遞給標準vprintf函數。而這又會要求您修改列表中的每個成員。但是,由於沒有元素明智的獲取/編輯/插入操作這樣的列表卡住了。

我真的沒有看到這樣做的任何方式。當然,您可以創建一個vprintf應用轉換原地,一次一個參數。我的建議是:重新實現全部這樣的標準庫函數 - 無論如何你正在編寫包裝。這涉及到一些工作,但你已經在做hc_printf等的一部分,所以爲什麼不去整個距離(並猜測保存在函數調用中的東西!)。

+0

重新實現vprintf和朋友可能是最正確的解決方案。我希望有一些方法可以用va_arg修改va_list中的值,但我想這樣的事情是不可能的。我可能必須決定是否重新實現vprintf和朋友,還是爲每個平臺都有一些特定於平臺的解決方案。 – 2010-02-06 01:13:53

3

您可能無法以與平臺無關的方式使用va_list。你必須看看你的環境如何在stdarg.h中定義一個va_list,然後編寫你自己的工具來處理它。

例如,如果一個va_list只是一個(char *),你可以用它做各種事情。

// add 1000 to the integer stored on the stack and advance va_list 
*(int *)va_list += 1000; 
va_list += sizeof(int); 

你告訴你希望它考慮的va_list的指針爲int(通過INT *鑄)編譯器,然後乘值(*),並加1000到它(+ = 1000) 。現在將va_list指針前移到堆棧中的下一個參數。

+0

這就是我目前使用的,所以+1。它適用於Linux中的x86-32。但是,我最終需要將這些代碼移植到ARM和x86-64上,我相信這兩種體系結構都會在寄存器中傳遞參數,這可能會使這種方法變得更加複雜。 – 2010-02-06 01:09:48