假設我有以下代碼,定義了一個結構和一個聯合:
struct Foo
{
int i;
float f;
};
union Bar
{
struct Foo foo;
char buf[10];
};
將指標分配給型別物件而不是型別struct Foo指標union Bar并隨后使用它是否安全:
struct Foo fooVar;
union Bar* barPtrVar = (union Bar*) &fooVar;
bar->foo.i = 1000;
bar->foo.f = 0.5f;
只要我們確定只使用了foo成員union Bar?
我記得在 C 中,指向結構的指標與指向它的第一個成員的指標相同,因此它們有時可以互換,因此盡管類似的邏輯可能適用于此。
uj5u.com熱心網友回復:
這種用法是安全的,即使您從buf成員那里讀取(直到某一點)。
C 標準的第 6.5p7 節給出了以下別名規則:
物件的存盤值只能由具有以下型別之一的左值運算式訪問:
- 與物件的有效型別兼容的型別,
- 與物件的有效型別兼容的型別的限定版本,
- 與物件的有效型別相對應的有符號或無符號型別,
- 對應于物件有效型別的限定版本的有符號或無符號型別,
- 聚合或聯合型別,在其成員中包括上述型別之一(遞回地包括子聚合或包含聯合的成員),或
- 一種字符型別。
您的用例屬于倒數第二個要點,并且根據最后一個要點允許從buf成員讀取到。buf[sizeof(struct Foo)-1]
uj5u.com熱心網友回復:
一般來說,這是不安全的。聯合型別的對齊大小至少與其成員的最大對齊大小一樣大。因此,union Bar包含型別成員的 astruct Foo可能比 a 具有更嚴格的對齊方式struct Foo。(這在 OP 的示例中不太可能,因為 的其他成員union Bar的對齊大小為 1,但通常情況下確實如此。)
考慮代碼序列:
struct Foo fooVar;
union Bar* barPtrVar = (union Bar*) &fooVar;
如果union Bar具有比 更嚴格的對齊方式struct Foo,則此轉換將導致未定義的行為,根據C11 6.3.2.3/7(強調我的):
指向物件型別的指標可以轉換為指向不同物件型別的指標。如果結果指標未正確對齊68)對于參考的型別,則行為未定義。否則,當再次轉換回來時,結果將等于原始指標。當指向物件的指標轉換為指向字符型別的指標時,結果指向物件的最低尋址位元組。結果的連續增量,直到物件的大小,產生指向物件剩余位元組的指標。
運算式barPtrVar->foo(型別struct Foo)是有問題的,因為后面的識別符號->指定了結構或聯合物件的成員,但barPtrVar實際上并不指向 aunion Bar所以barPtrVar->foo實際上不是結構或聯合物件的成員,根據 C11 6.5.2.3/ 4(強調我的):
后綴運算式后跟
->運算子和識別符號指定結構或聯合物件的成員。該值是第一個運算式指向的物件的命名成員的值,并且是一個左值。96)如果第一個運算式是指向限定型別??的指標,則結果具有指定成員型別的限定版本。
此外,根據C11 6.2.6.1/7barPtrVar->foo ,在(型別)中存盤值struct Foo也可能會修改union Bar不屬于成員的位元組:foo
當一個值存盤在聯合型別物件的成員中時,物件表示中不對應于該成員但對應于其他成員的位元組采用未指定的值。
由于barPtrVaris 不指向 a union Bar,而是指向較小的 ,因此假定成員(型別)邊界之外的struct Foo位元組可能會采用未指定的值,從而導致未定義的行為,因為這些位元組實際上不是 a 的一部分。foostruct Foounion Bar
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/519687.html
標籤:C指针结构c99工会
上一篇:在C中的編譯時將空陣列分配給指標
