[英]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.