簡體   English   中英

導致非標准行為的 #pragma 是否會導致 __STDC__ 宏未定義為 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.

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