簡體   English   中英

如何確定 va_arg 列表的結尾?

[英]How to determine the end of va_arg list?

我有一個 function foo(char *n, ...); 我需要獲取並使用所有可選的char參數。 我有一個想法使用

while(va_arg(argPtr, char) != NULL)
{
   ...
}

了解我何時到達列表末尾。 那么,它會工作嗎,如果在 function 電話中我會做foo(n, 't', 'm', '$', NULL);

NULL會被 va_arg 讀取為 char 嗎? 或者也許有一種更常見的方法來確定列表的結尾,而不添加NULL作為最后一個參數?

對於使用va_arg來確定給定調用傳遞的參數的數量或類型的函數,沒有直接的方法。

特別是您提出的方法:

while(va_arg(argPtr, char) != NULL)

不正確。 va_arg(argPtr, char)產生一個char類型的值,而NULL是一個空指針常量。 NULL通常定義為0 ,它相當於空字符'\\0' ,但你不能依賴它。)

任何可變參數函數都必須有一種方法供調用者指定參數的數量和類型。 例如, *printf函數通過(非可變參數)格式字符串執行此操作。 POSIX execl*()函數需要一系列char*參數; 參數列表的末尾由調用者用(char*)NULL標記。 其他方法也是可能的,但它們幾乎都依賴於運行時參數中給出的信息。 (您可以使用其他方法,例如全局變量。請不要。)

這給調用者帶來了負擔,以確保傳遞給函數的參數是一致的。 函數本身無法確認這一點。 不正確的調用,如printf("%d\\n", "hello")execlp("name", "arg1")具有未定義的行為

還有一件事:您不能將va_argchar類型的參數一起使用。 當您調用可變參數函數時,對應於, ...參數會被提升 類型比int窄的整數參數被提升為intunsigned int ,而float類型的參數被提升為double 如果調用者傳遞char類型的參數,則該函數必須調用va_arg(argPtr, int)

(在您不太可能遇到的非常模糊的情況下, char可以提升為unsigned int 。只有當普通char是 unsigned 並且sizeof (int) == 1才會發生這種情況,這意味着一個字節至少為 16位。)

在這一點上只是晚了幾年,但我有一些有趣的貢獻。 在這種情況下,我會使用預處理器。 順便說一下,警告變量類型不正確。

注意:用括號括起 function 名稱將使宏忽略它,同時允許文件符號與 API 相同(宏用法除外)。

方法一:哨兵宏

像這樣掩蓋你的功能:

#define my_func(...) my_func(__VA_ARGS__, NULL);
void (my_func)(...)
{
   /* ... */
}

方法二:數組長度宏

將可變參數轉換為int[] ,以靜態確定所述數組的sizeof

#define ARRAY_LENGTH(a) (sizeof(a) / sizeof((a)[0]))              
#define NARGS(...) ARRAY_LENGTH((int[])({ __VA_ARGS__ }))

類似mask,只是在前面加一個count參數:

#define my_func(...) my_func(NARGS(__VA_ARGS__), __VA_ARGS__);
void (my_func)(int n, ...)
{
   /* ... */
}

數組宏的編譯特定類型檢查:

在問題的 scope 之外,但由於我在滾動

我總是使用 GCC,有時使用以下。 我相信 Clang 中有半兼容功能,但我不確定。 這使您可以確保傳遞的是實際數組而不是指針。

#define TYPE_COMPATABLE(x, y) __builtin_types_compatible_p(__typeof__(x), __typeof__(y))
#define CHOOSE_EXPR(b, x, y) __builtin_choose_expr((b), (x), (y))
#define IS_ARRAY(a) (!TYPE_COMPATABLE((a), &(a)[0]))
#define ARRAY_LENGTH(a)                           \
    ({                                            \
        _Static_assert(IS_ARRAY(a), "non-array"); \
        sizeof(a) / sizeof((a)[0]);               \
    })

#define NARGS(...) ARRAY_LENGTH((int[])({ __VA_ARGS__ }))

到時候,可能就用 C++ 呵呵。

基本的想法是可行的。 但是您以幾乎肯定不會的方式填寫了詳細信息。

當您使用可變參數時,通常的隱式轉換規則不適用。 相反,會發生默認參數提升,這意味着任何小於int / unsigned int整數類型都會被轉換為其中之一——這不是唯一的提升,而是這里唯一相關的——這也意味着沒有自動轉換為您使用va_arg指定的任何類型。

這意味着:

  • 您永遠不能使用va_arg(..., char) ,因為不可能將char作為可變參數函數參數傳遞。
  • 您永遠不能將NULL作為可變參數函數參數傳遞,因為它的具體類型在很大程度上依賴於實現。 現實類型是intlongvoid * ,還有許多其他不太現實但同樣有效的類型。

你可以改變

while(va_arg(argPtr, char) != NULL)

while(va_arg(argPtr, int) != 0)

和電話

foo(n, 't', 'm', '$', NULL);

foo(n, 't', 'm', '$', 0);

暫無
暫無

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

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