2017-02-21 50 views
4

我注意到一些潛在的問題va_arg宏,用於從可變參數函數接收未命名的參數。考慮以下,簡化的示例:當va_arg收到指針參數時,const不匹配會調用UB嗎?

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

void foo(int n, ...) 
{ 
    va_list ap; 
    const char *s; 

    va_start(ap, n); 
    for (s = va_arg(ap, const char *); *s != '\0'; s++) 
     putchar(*s); 
    va_end(ap); 
} 

int main(void) 
{ 
    char str[] = "xyz"; 
    foo(1, str); 
    return 0; 
} 

va_arg宏狀態的引用,(重點煤礦):

如果當在AP沒有更多的參數的va_arg被調用,或者如果 類型的在AP(促銷後)下一個參數不兼容 與T,該行爲是未定義(...)

我ü理解是const char *char *類型是不是兼容。當char數組傳遞到foo,在char指針的形式(即默認參數提升不變),則表達式,即假定常量限定指針:

s = va_arg(ap, const char *) 

可以調用「技術」 UB。當arr被定義爲const數組,並且該參數被接收爲char *以及其他類型例如int *const int *

+0

在這種情況下,不兼容是尺寸問題,而不是'const'問題。當大小不正確時,您將移動va_args掃描並嚴重崩潰。在64位機器上傳遞0而不是'NULL'時曾經有過。 –

回答

3

n1570/6.2.5p28似乎表明這應該在實踐中是好的:

......同樣,指向兼容類型的合格或不合格的版本應具有相同的表示和對齊要求...

由於char與自身兼容,指向char的指針與指向const char的指針具有相同的表示形式。由於變量參數函數本質上假定每個後續參數的表示,因此首先出現在定義良好的位置。

至於周圍的其他方法,如果你使用字符指針,以修改一個const char,這確實是UB每n1570/6.7.3p6

如果試圖修改與定義的對象通過使用具有非const限定類型的左值的const限定類型,行爲是未定義的。


要繼續從您的評論鏈接到答案的分析,n1570/6.7.6.1p2

要使兩種指針類型兼容,兩者應是相同的合格,雙方應指針兼容的類型。

因爲這兩個指針類型本身是非常量,它們是相同的資格,剩下的唯一問題是,如果const charchar我們的目的兼容。

看起來他們不是......就像nickie指出的那樣。所以一方面,這可以被解釋爲UB。

+1

我同意這裏沒有真正的問題,因爲這兩個指針必定有相同的表示。但是如果它是相反的(一個'const char *'被視爲一個'char *'並且可能被分配給),我會對未定義的行爲感到高興。然而,根據該標準,這似乎不是一個表示問題,而是類型兼容性問題,正如這裏定義的那樣。 – nickie

+0

我的假設是基於C11 6.7.3/10的:'對於兩個符合要求的合格類型,兩者應具有相同類型的合格版本;',以及[this](http://stackoverflow.com/a/18458745/586873)回答。 Neverthless,我同意,它更像是理論問題,因爲'const'表示是相同的,正如你所指出的那樣。 –

+0

@GrzegorzSzpetkowski - 但你沒有兩個限定類型。指針是不合格的,只有pointees是。這個標準對此太模糊...... – StoryTeller

1

是的,它們不兼容,當實際類型爲char *時,使用類型爲const char *的va_arg,反之亦然,是未定義的行爲。

一種類型只與其自身兼容。

此外,關於限定符的部分(const爲1)支持此。


(來自ISO/IEC引用9899:201X)

(6.2.7兼容型和複合型1)
兩種類型的具有兼容的類型,如果其類型是相同的

(6.7.3類型限定符10)
對於兩個限定類型是兼容的,兩者應具有兼容的相同合格 版本類型;說明符或限定符 列表中的類型限定符的順序不會影響指定的類型。

相關問題