我知道 ODR、鏈接static、和如何extern "C"使用函式。但是我不確定型別的可見性,因為它們不能被宣告static并且 C 中沒有匿名命名空間。
特別想知道以下代碼編譯成C和C 的有效性
// A.{c,cpp}
typedef struct foo_t{
int x;
int y;
} Foo;
static int use_foo()
{
Foo f;
f.x=5;
return f.x;
}
// B.{c,cpp}
typedef struct foo_t{
double x;
} Foo;
static int use_foo()
{
Foo f;
f.x=5.0;
return f.x;// Cast on purpose
}
使用以下兩個命令(我知道兩個編譯器都會根據擴展名自動檢測語言,因此名稱不同)。
g -std=c 17 -pedantic -Wall -Wextra a.cpp b.cppgcc -std=c11 -pedantic -Wall -Wextra a.c b.c
8.3 版愉快地編譯了兩者,沒有任何錯誤。顯然,如果兩個結構符號都具有外部鏈接,則會違反 ODR,因為定義不相同。是的,編譯器不需要報告它,因此我的問題是因為兩者都沒有。
它是有效的 C 程式嗎?
我不這么認為,這就是匿名命名空間的用途。
它是有效的 C 程式嗎?
我在這里不確定,我已經讀過認為型別static可以使程式有效。有人可以確認嗎?
C、C 兼容性
如果這些定義在公共頭檔案中,也許在不同的 C 庫中,并且 C 程式包含兩者,每個都在不同的 TU 中,那會是 ODR 嗎?如何防止這種情況發生?有extern "C"什么作用嗎?
uj5u.com熱心網友回復:
我將使用 C 語言的 C11 的 n1570 草案和 C 語言的 C 20 的 n4860 草案作為參考。
C語言
型別在 C 中沒有鏈接:6.2.2 識別符號的鏈接 §6:
以下識別符號沒有鏈接:宣告為物件或函式以外的任何識別符號的識別符號...
這意味著 ac 和 bc 中使用的型別是不相關的:您在兩個編譯單元中正確宣告了不同的物件。
C 語言
型別在 C 中確實有鏈接。6.6 程式和鏈接 [basic.link] 說(強調我的):
- §2:
如果名稱可能表示與另一個作用域中的宣告引入的名稱相同的物件、參考、函式、型別、模板、名稱空間或值,則稱該名稱具有鏈接
- §4
未命名命名空間或在未命名命名空間內直接或間接宣告的命名空間具有內部鏈接。所有其他命名空間都有外部鏈接。具有名稱空間范圍的名稱未在上面給出內部鏈接,并且是
...
一個命名類的名稱...
...
其鏈接確定如下:
- 如果封閉的名稱空間具有內部鏈接,則名稱具有內部聯動;
— 否則,如果名稱的宣告附加到命名模塊(10.1)并且未匯出(10.2),則名稱具有模塊鏈接;
— 否則,名稱具有外部鏈接a.cpp 和 b.cpp 中宣告的型別與外部鏈接共享相同的識別符號并且不兼容:程式格式錯誤。
話雖如此,大多數常見的編譯器都能夠編譯 C 或 C 源代碼,我敢打賭,他們努力共享這兩種語言的大部分實作。出于這個原因,我相信現實世界的實作即使對于 C 語言也能產生預期的結果。但是未定義的行為并不禁止預期的結果......
uj5u.com熱心網友回復:
對于 C。程式是有效的。這里唯一適用的要求是“嚴格別名規則”,即只能通過兼容型別的左值訪問物件( 6.5p7 中描述的一些例外)。
在單獨的翻譯單元中定義的結構/聯合的兼容性在6.2.7p1 中定義。
... 如果它們的標簽和成員滿足以下要求,則在單獨的翻譯單元中宣告的兩個結構、聯合或列舉型別是兼容的:如果一個用標簽宣告,另一個應用相同的標簽宣告。如果兩者都在各自翻譯單位內的任何地方完成,則適用以下附加要求:其成員之間應有一對一的通信這樣每一對對應的成員都用兼容的型別宣告;如果對中的一個成員使用對齊說明符宣告,則另一個成員使用等效對齊說明符宣告;如果對中的一個成員使用名稱宣告,則另一個成員使用相同的名稱宣告。對于兩個結構體,相應的成員應以相同的順序宣告。對于兩個結構或聯合,相應的位域應具有相同的寬度。對于兩個列舉,對應的成員應具有相同的值。
因此,示例中的結構不兼容。
但是,這不是問題,因為f物件是通過本地定義的型別創建和訪問的。如果物件是使用Foo一個翻譯單元中定義的型別創建并通過Foo另一個翻譯單元中的其他型別訪問的,則將呼叫 UB :
// A.c
typedef struct foo_t{
int x;
int y;
} Foo;
void bar(void *f);
void foo()
{
Foo f;
bar(&f);
}
// B.c
typedef struct foo_t{
double x;
} Foo;
// using void* to avoid passing pointer to incompatible types
void bar(void *f_)
{
Foo *f = f_;
f->x=5.0; // UB!
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/327470.html
上一篇:撰寫程式的最聰明方法,該程式讀取20到89之間的數字N并列印其表示為字串的值
下一篇:a[0]指向哪里?
