![](/img/trans.png)
[英]Why Clang running on Windows has no C conformance (__STDC__ is not defined to 1)?
[英]Shall a #pragma leading to nonstandard behavior cause __STDC__ macro not to be defined to 1?
簡單的問題:導致非標准行為的#pragma
是否會導致__STDC__
宏未定義為 1? (C 標准是否明確規定了這一點?如果是,那么在哪個部分?如果不是,那為什么?) 問題原因:見下文。
示例代碼(t28.c):
#pragma warning( disable : 34 )
typedef int T[];
int main()
{
int rc = sizeof(T);
#if __STDC__ == 1
rc = 0;
#else
rc = 1;
#endif
return rc;
}
調用: cl t28.c /std:c11 /Za && t28; echo $?
cl t28.c /std:c11 /Za && t28; echo $?
預期結果: 1
實際結果: 0
編譯器版本:
cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29913 for x64
注意:C11(6.5.3.4 sizeof 和 _Alignof 運算符)(已添加重點):
sizeof 運算符不應應用於具有 function 類型或不完整類型的表達式,...
在這里,我們看到#pragma
導致非標准行為:違反“shall 要求”,未生成診斷消息,調用編譯器的后端,生成並成功執行.exe
。 但是,這種非標准行為不會導致__STDC__
宏未定義為1
。
問題的原因:測試。 一項測試,類似於t28.c
失敗,因為它期望返回碼1
( __STDC__
未定義為1
)。 系統的哪個部分包含錯誤:測試或編譯器(或兩者)?
在這里,我們看到
#pragma
導致非標准行為:違反“shall 要求”,未生成診斷消息,調用編譯器的后端,生成並成功執行.exe
。 但是,這種非標准行為不會導致__STDC__
宏未定義為 1。
首先, __STDC__
宏並不是一種報告“在此編譯期間發生的導致非標准行為的事情”的機制。 它不是以那種方式動態(變化)的。 對__STDC__
的渴望只是為了報告 C 實現符合要求。 (這只是一個願望,因為 C 標准可能要求符合的實現將__STDC__
定義為 1,但無法控制不符合的實現將其定義為什么。)如果實現符合要求, __STDC__
將始終為 1,無論在任何特定的編譯。
(請注意,一個編譯器可能包含多個 C 實現,這些實現由各種命令行開關選擇,例如為各種類型請求不同的大小、啟用或禁用符合標准的擴展、啟用來自 C 標准的不符合標准的變體,等等。開關可能導致__STDC__
被定義為 1,而其他開關則沒有。)
其次, #pragma warning( disable: 34 )
不會導致非標准行為。 根據 C 2018 6.10.6 1,該指令“導致實現以實現定義的方式運行。 該行為可能會導致翻譯失敗或導致翻譯器或生成的程序以 [anotherly] 不合格的方式運行。” 因此,假設實現記錄了 pragma 以抑制有關違反 6.5.3.4 1 中有關sizeof
運算符的約束的警告,那么 C 標准允許這樣做。 6.10.6 1 中的此規則覆蓋了 6.5.3.4 1 中的約束。該行為是 C 標准允許的,因此符合要求。
標准保留#pragma STDC...
用於未來的語言擴展 - 所有其他 pragma 都是實現定義的 (C17 6.10.6)。
不要混淆哪個__STDC__
,它設置為 1 以將編譯器標記為符合標准的實現(C17 6.10.8.1)。
該術語又在 C17 4/5 和 4/6 中定義:
嚴格遵守的程序應僅使用本國際標准中指定的語言和庫的那些特性
兩個符合要求的 forms 是托管和獨立的。 符合要求的托管實現應接受任何嚴格符合要求的程序。 /// 一個符合的實現可以有擴展(包括額外的庫函數),只要它們不改變任何嚴格符合程序的行為。
這並不意味着只要應用程序是嚴格符合的程序就設置__STDC__
,而是如果編譯器及其當前選項是符合要求的實現,則將其設置為固定為 1 或 0。
例如,編譯器可能包含 POSIX 庫,這些庫將非標准標識符轉儲到標准頭文件中。 如果這些標識符通過命名空間沖突影響嚴格符合程序的行為,則不應將__STDC__
設置為 1。一個不符合的示例是當我使用gcc -std=gnu17
編譯它時:
#include <string.h>
#if __STDC__== 1
int strdup; // file scope declaration
#endif
我得到“錯誤:'strdup'被重新聲明為不同類型的符號”。 這是不合規的行為。 可以說,在-std=gnu17
下運行時__STDC__
設置為 1 是一個錯誤,因為這不是一個符合要求的實現。
如果我切換到-std=c17
,它會按照應有的方式干凈地編譯 - 然后 gcc 是一個符合要求的實現。
否則,正如我們從上面引用的部分中可以看出的那樣,符合要求的實現仍然可以具有擴展。
在您的特定情況下,您選擇禁用診斷作為非標准擴展。 這是程序員的調用,而不是使編譯器不兼容的東西。 它只是使應用程序不符合標准,但編譯器仍然可以編譯一個嚴格符合標准的程序,即使您沒有提供它。
如果您想演示 VS 的不一致性,那么這是一個更好的程序:
#include <stdio.h>
int main()
{
int x = __STDC__;
#if defined(__STDC_NO_VLA__) && __STDC_NO_VLA__==1
int arr [2];
#else
int arr [x];
#endif
printf("%zu", sizeof arr / sizeof *arr);
}
如果支持 VLA,則符合標准的實現應打印 1,否則為 2。損壞的實現將給出大量奇怪的診斷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.