簡體   English   中英

將struct指針轉換為另一個struct

[英]Cast struct pointer to another struct

此代碼段打印值5 我不明白為什么。

#include <stdio.h>

struct A
{
    int x;
};

struct B
{
    struct A a;
    int y;
};

void printA(struct A *a)
{
    printf("A obj: %d\n", a->x);
}

int main()
{
    struct B b = {
        {
            5
        },
        10
    };

    struct A *a = (struct A*)&b;
    printA(a);

    printf("Done.\n");

    return 0;
}

當我創建b ,指向它的指針將指向數據{ {5}, 10 }

當我將&bstruct A* ,我向編譯器保證這個struct A*指向數據類型為int的單個數據元素的結構。 相反,我提供了一個指向數據類型struct Aint的兩個數據元素的結構的指針。

即使第二個變量被忽略(因為struct A只有一個數據成員),我仍然提供一個結構,其成員的數據類型為struct A ,而不是int

因此,當我通過在aprintA ,線a->x進行,基本上,要求訪問的第一數據元素a 的第一數據元素a的數據類型的struct A ,這是一種類型不匹配,由於%d期待一個數字,而不是一個struct A

到底發生了什么?

當我創建b ,指向它的指針將指向數據{ {5}, 10 }

是的,從某種意義上講,它是適合類型且價值正確的C初始化程序的文本。 這文字本身不應該按字面意思理解為結構的價值。

當我將&bstruct A* ,我向編譯器保證這個struct A*指向數據類型為int的單個數據元素的結構。

不,不完全是。 您正在表達式&b的值轉換為類型struct A * 結果指針實際指向struct A是一個單獨的問題。

相反,我提供了一個指向數據類型struct Aint的兩個數據元素的結構的指針。

不,不是“反而”。 鑒於struct B的第一個成員是struct A ,並且C禁止在結構的第一個成員之前填充,指向struct B的指針指向struct A - B的第一個成員 - 在一般意義上。 正如@EricPostpischi在注釋中觀察到的那樣,C標准在您的特定情況下明確指定結果:給定struct B b ,將指針b轉換為類型struct A *產生指向b的第一個成員的struct A ,即struct A

即使第二個變量被忽略(因為struct A只有一個數據成員),我仍然提供一個結構,其成員的數據類型為struct A ,而不是int

struct B表示的第一個sizeof(struct A)字節構成其第一個成員的表示形式,即struct A 后者是前者的成員除了在記憶中的重疊之外沒有任何物理表現。

即使語言沒有明確指定它,給定你的變量b作為struct B聲明,也沒有實際的理由期望表達式(struct A*)&b == &b.a將評估為false,並且毫無疑問,右手指針可用於訪問struct A

因此,當我通過在一個給printA ,線a->x進行,基本上,要求訪問的第一數據元素a

是的,這就是一個斷言進入那a確實指向一個struct A 正如您已經討論的那樣,它在您的情況下做了什

的第一數據元素a是數據類型的struct A

根據定義, *astruct A 具體來說,它是struct A其表示與b的表示的開頭重疊。 如果沒有這樣的struct A那么行為將是未定義的,但這不是問題。 像每個struct A ,它有一個由x指定的成員,它是一個int

這是一種類型不匹配,因為%d期望數字,而不是struct A

你的意思是期待一個int 這就是它的結果。 這就是表達式a->x讀取的內容,假設行為是完全定義的,因為這是該表達式的類型。 在不同的情況下,行為可能確實沒有定義,但在任何情況下,該表達式都不會提供struct A

到底發生了什么?

似乎正在發生的事情是你正在想象比C實際提供的更高級別的語義。 特別是,您似乎將結構的心智模型作為可區分成員對象的列表,這會導致您形成不正確的期望。

也許您更熟悉一種弱類型語言,如Perl,或動態類型語言,如Python,但C的工作方式不同。 您無法查看C對象並且有用地詢問“您的類型是什么”? 相反,您通過用於訪問它的表達式的靜態類型的鏡頭來查看每個對象。

語言 - 律師解釋為什么代碼是好的:

  • C中的任何指針都可以轉換為任何其他指針類型。 (C176.3.2§7)。
  • 如果在轉換后取消引用指向對象是安全的,則取決於:1)如果類型是兼容的,從而正確對齊,2)如果允許使用的相應指針類型為別名。
  • 作為一種特殊情況,指向結構類型的指針等效於指向其第一個成員的指針。 C176.7.2§15的相關部分說:

    指向適當轉換的結構對象的指針指向其初始成員(或者如果該成員是位字段,則指向它所在的單元),反之亦然。

  • 這意味着(struct A*)&b很好。 &b適當地轉換為正確的類型。

  • 沒有違反“嚴格別名”,因為我們符合C176.5§7:

    對象的存儲值只能由具有以下類型之一的左值表達式訪問:

    • 與對象的有效類型兼容的類型,...
    • 聚合或聯合類型,包括其成員中的上述類型之一

    初始成員的有效類型是struct A 在print函數中發生的左值訪問很好。 struct B也是一種聚合類型,在其成員中包含struct A ,因此無論頂部引用的初始成員規則如何,都不可能發生嚴格的別名沖突。

對於這種情況,C標准中有一條特殊規則。 C 2011 6.7.2.1 15說:

指向適當轉換的結構對象的指針指向其初始成員(或者如果該成員是位字段,則指向它所在的單元),反之亦然。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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