簡體   English   中英

va_list 總是 static 嗎?

[英]Is va_list always static?

void    va_test2(va_list ap2)
{
    printf("va_test 2 : %d\n", va_arg(ap2, int));
}
void    va_test1(const char* str, ...)
{
    va_list ap1;
    printf("%s\n", str);
    va_start(ap1, str);
    va_test2(ap1);
    printf("va_test 1 : %d\n", va_arg(ap1, int));
    va_test2(ap1);
}
int     main(void)
{
    va_test1("this is a test", 1, 2, 3);
}
result :
    this is a test
    va_test 2 : 1
    va_test 1 : 2
    va_test 2 : 3
result I expected:
    this is a test
    va_test 2 : 1
    va_test 1 : 1
    va_test 2 : 2

在我看來,在'va_test1'中初始化va_list'ap1'之后,它被復制到'va_test2'中的局部變量'ap2'中。

所以 va_arg(ap, int) 增加了 'va_test2' 中的 va_list 'ap2' 后,應該不會影響原來的 va_list 'ap1'。

但行為表明增加的參數實際上影響了“ap1”。

據我所知, va_arg 被定義

#define va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))

這直接增加了發送的指針。

在我的結論中, va_list 似乎 static 行為無論在哪里聲明。

你能告訴我這是對的嗎,為什么它顯示 static 行為?

va_list 總是 static 嗎?

不它不是。 它通常是本地的。

您的代碼的行為未定義。 調用后va_test2(ap1); ap1唯一能做的就是調用va_end(ap1) 我們可以閱讀C11 7.16p3

[...] object ap 可以作為參數傳遞給另一個 function; 如果 function 使用參數 ap 調用 va_arg 宏,則調用 function 中的 ap 的值是不確定的,應在進一步引用 ap 之前傳遞給 va_end 宏。

不過,您的代碼行為可以通過拒絕您的va_arg定義來解釋。 您得到的行為與宏的顯示定義不匹配,因為宏確實修改了變量的值(除非有隱藏的#define ap *ap :),因此拒絕該定義將是前進的方式.

據我所知, va_arg 被定義

#define va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))

我會說,很可能不是。 如果您使用的是 x86 架構,則使用不同的寄存器傳遞不同的數據類型,如x86 abi所指定的(例如,參見第 21 頁和第 52 頁周圍的整個部分)(並參見此答案)。 該定義很可能適用於一些有限的情況。 如今, va_arg通常是一些編譯器魔法,比如gcc/stdarg.h __builtin_va_arg

如果va_list是數組類型或指向堆棧上數據的指針,則可以解釋該行為。 x86上,它是一個結構的數組,如上面的 abi 中所定義。

// cross my fingers these are right
typedef int va_list[1];
#define va_start(ap, a)   (*ap = (int*)&a);
#define va_arg(ap, t)     (*(t*)((*ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))

因為你的代碼的行為沒有定義,編譯器實現者只是不關心這些代碼的行為——它可以以任何方式表現。 因此,你不能得到result I expected: - 你不能期望從這樣的代碼中得到任何東西 - 通常期望鼻惡魔產卵

這里沒有證據表明ap1具有 static 存儲持續時間。 只有證據表明va_test2可以訪問ap1中或引用的數據,這可能是因為ap1是一個數組或指向數據的指針或包含此類指針的結構。

不,它不能是 static。 原因是 static 將不允許 function 可重入,因此您不應在不同的線程中使用可變參數 function。

va_list是一個普通類型,就像一個指針。 唯一的區別是它是一個非常特殊的指針,它指向參數列表中的變量,事實上,根據 ABI,它可能非常特殊(因為它應該引用例如寄存器,以防 ABI 允許在寄存器中傳遞參數)

在古老的 C 編譯器中, va_list是一個簡單的指針,它被轉換為指向您傳遞給va_arg宏的類型的指針,以便能夠對其進行指針運算並將其推進以指向列表中的下一個參數。 這些是與流程相關的語義。 但是這個指針算法一定是特殊的,因為不同的cpu更新指向堆棧的指針通常以不同於普通數據指針的方式對齊數據。 這意味着,例如,如果您在 64 位架構中傳遞一個短數字或一個int (一個 32 位整數),則可能會將一個完整的 64 位字推入堆棧以保存單個字符(或整數),例如效率原因。 無論如何,它是一個架構最依賴的部分,這也是大多數編譯器對其進行特殊處理的原因(在 gcc 中,它映射到__gnuc_va_list )。 但本質上它是一個指針(可能通過引用傳遞,你不知道)。

在 C 中有一種通過引用傳遞變量的方法,它包括將變量聲明為該變量的一個元素的數組。 我在實際參數列表中寫入數組名稱,您確實是通過引用傳遞變量,因為變量名稱是對只有一個元素的數組的第一個元素的引用:

typedef void *va_list[1];
#define va_start(_l, _first) do{_l[0] = &(_first)+1;}while(0)
#define va_arg(_l, _typ) (*(_typ *)(_l[0] = (_typ *)_l + 1));
#define va_end(_l)  /* just nothing */

上面的代碼將為您提供通過引用傳遞的指針的語義,當您將它作為參數傳遞給 function 時(因為您已經定義了類型,當您通過名稱傳遞它時,您使用的是引用)

#include <stdio.h>

/* these are implemented in a library and the user doesn't
 * see the details on how my_type is defined. */
typedef int va_list_fake[1];

void va_start_fake(va_list_fake a, int val)
{
    a[0] = val;
}

int va_arg_fake(va_list_fake a)
{
    return a[0];
}

void exchange(va_list_fake a, va_list_fake b)
{   /* this is not seen by the function user */
    int temp = a[0];
    a[0] = b[0];
    b[0] = temp;
}

void va_end_fake(va_list_fake a)
{
    /* empty */
}

/* now it comes the code in main that doesn't know how is
 * implemented the type va_list_fake */
int main()
{
    va_list_fake a, b;  /* they look as normal variables */

    va_start_fake(a, 3); /* compare this with va_start */
    va_start_fake(b, 2); /* idem. */

    /* print the contents of variables a and b with a simil
     * of function/macro va_arg() that differs from va_arg only
     * in the lack of a second type parameter. */
    printf("a = %d, b = %d\n", va_arg_fake(a), va_arg_fake(b));

    /* exchange, but pass the variables by value, so it should
     * not be updated.  8-. (but there's some hidden trick) */
    exchange(a, b);  /* this will exchange the values */

    /* now a contains the value 2 and b contains the value 3 */
    printf("a = %d, b = %d\n", va_arg_fake(a), va_arg_fake(b));

    va_end_fake(a);
    va_end_fake(b);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM