2017-06-13 47 views
16

訪問子變量如果我有這些結構:通過更高一級的結構

typedef struct { int x; } foo; 
typedef struct { foo f; } bar; 

通常你會通過b.f.x訪問x,但有一種方法來設置它,這樣就可以不參考訪問元素xf

bar b; 
b.x = ... 

我的第一直覺是,你不能因爲將是名稱衝突的可能性,如果兩個子結構均具有一員x和我想不通的編譯錯誤是什麼。但是,我記得在一些可能的框架中工作。

在C++中,我曾在一個框架中工作過,其中bar存在,並且您可以從其他類訪問其成員作爲成員變量this->x。我試圖弄清楚如何做到這一點。

回答

18

您可以使用C11:

§6.7.2.1 - 11

一位不願具名的成員,其類型說明符是結構符不帶標籤被稱爲 匿名結構;一個未命名的成員,其類型說明符是與 沒有標籤的聯合說明符稱爲匿名聯合。匿名結構或工會成員 被視爲包含結構或工會的成員。如果包含結構或聯合也是匿名的,則這將遞歸地應用 。

所以這個代碼威力工作:

#include <stdio.h> 

typedef struct { int x; } foo; 
typedef struct { foo; } bar; 

int main(void) 
{ 
    bar b; 
    b.x = 1; 
    printf("%d\n", b.x); 
} 

這裏的問題是,不同的編譯器在我的測試中不同意在的typedef是否是因爲沒有標籤的一個結構說明符可以接受的標準規定:

§6.7。8 - 3

在聲明其存儲類說明是typedef,每個聲明符定義一個 標識符是表示 在6.7.6中描述的方式使標識符指定的類型typedef名。 [...] A typedef聲明不會引入新的類型,只有a 類型如此指定的同義詞。

(重點煤礦) - 但這是否代名詞也意味着typdef名符是可交換的結構符gcc接受這個,clang沒有。

當然,有沒有辦法來表達foo類型與這些聲明的全會員,你犧牲自己的命名成員f

關於你有關名稱衝突疑問,這是什麼gcc有,當你把另一int xbar說:

structinherit.c:4:27: error: duplicate member 'x' 
typedef struct { foo; int x; } bar; 
         ^

爲了避免歧義,你可以重複的結構,可能#define d爲宏,但當然,這看起來有點醜:

#include <stdio.h> 

typedef struct { int x; } foo; 
typedef struct { struct { int x; }; } bar; 

int main(void) 
{ 
    bar b; 
    b.x = 1; 
    printf("%d\n", b.x); 
} 

任何符合標準的編譯應該接受這個代碼,所以堅持這個版本。

<意見>這是一個遺憾,我喜歡gcc更好的接受語法,但作爲標準的措詞並不能使明確來實現這一點,只有安全的賭注是假設這是禁止的,所以clang是不是在這裏怪... < /意見>

如果您想通過x要麼b.xb.f.x,你可以使用一個額外的匿名聯合這樣的:

#include <stdio.h> 

typedef struct { int x; } foo; 
typedef struct { 
    union { struct { int x; }; foo f; }; 
} bar; 

int main(void) 
{ 
    bar b; 
    b.f.x = 2; 
    b.x = 1; 
    printf("%d\n", b.f.x); // <-- guaranteed to print 1 
} 

這將因爲

§引起混淆的問題6.5.2.3 - 6

一個特殊的保證是由爲了簡化聯合的使用:如果聯合包含多個共享公共初始序列的結構(參見下文),並且聯合對象當前包含這些結構中的一個,則允許啓用例如,在任何可以看到完整類型的工會聲明的地方,都可以包含任何人的共同首字母部分。如果對應的成員對於一個或多個初始成員的序列具有兼容類型(並且對於位域,相同寬度),則兩個結構共享一個共同的初始序列

+0

夢幻般的迴應。這確實回答了這個問題。匿名結構是我正在尋找的,而且我很可能正在研究C++ 11特定的代碼。 – Stewart

+1

@Stewart注意我的整個迴應談論C11,**而不是C++ 11。我不知道這是否也適用於C++ 11。 –

2

這在C中是不可能的。然而,在C++中,您可以使用繼承,這可能是您正在考慮的。

0

在C++中,有兩種可能。首先是使用繼承。第二個用於bar包含名爲xint &x)的引用成員和初始化x以引用f.x的構造函數。

在C中,這是不可能的。

3

在C(99及以後)中,即使不是寫入的最後一個成員,也可以訪問工會成員的共同初始子序列。

在C11中,您可以擁有匿名聯盟成員。所以:

typedef struct { int x; } foo; 
typedef struct { 
    union { 
    foo f; 
    int x; 
    }; 
} bar; 

  1. 是的,適用於結構。但根據標準:
    • 一個適當轉換的結構指針指向第一個成員。
    • 經過適當轉換的聯合指針指向任何聯合成員。
    • 所以他們在內存中的位置是一樣的。
5

在C中,您不能訪問這樣的成員的成員。

但是,您可以匿名內部結構的訪問權限的成員:

struct bar { 
    struct { 
     int x; 
    } 
}; 

... 
struct bar b; 
b.x = 1; 

在C++中使用繼承:

struct foo { 
    int x; 
}; 

struct bar: public foo { 
}; 

... 
struct bar b; 
b.x = 1; 
2

在C++中,你可以使用繼承和成員名稱衝突有幾分解析與::並將基類視爲成員。

struct foo { int x; }; 
struct bar : foo { }; 

struct foo1 { int x; }; 
struct bar1 : foo1 { char const* x; }; 

bar b; 
bar1 b1; 
int main() 
{ 
    return b.x + b1.foo1::x; 
} 

在標準C,這是不可能的,但也有一些編譯器(GCC,鐺,tinycc)支持類似的事,作爲一個擴展名(通常與-fms-extensions訪問(海合會還與-fplan9-extensions這是-fms-extensions一個超集)) ,它允許你這樣做:

struct foo { int x; }; 
struct bar { struct foo; }; 
struct bar b = { 42 }; 
int main() 
{ 
    return b.x; 
} 

但是,沒有解決衝突的成員名稱與它,AFAIK。

0

由於C標準保證沒有一個結構的第一構件之前填充,沒有在barfoo之前填充,並沒有在foox之前填充。因此,訪問bar開始的原始內存將訪問bar::foo::x

你可以做這樣的事情:

#include <stdio.h> 
#include <stdlib.h> 

typedef struct _foo 
{ 
    int x; 
} foo; 

typedef struct _bar 
{ 
    foo f; 
} bar; 

int main() 
{ 
    bar b; 
    int val = 10; 

    // Setting the value: 
    memcpy(&b, &val, sizeof(int)); 
    printf("%d\n", b.f.x); 

    b.f.x = 100; 


    // Reading the value: 
    memcpy(&val, &b, sizeof(int)); 
    printf("%d\n", val); 
    return 0; 
} 

正如其他人指出,C++提供了通過繼承這樣做的更優雅的方式。

7

Ç:高度不推薦的,但是是可行的:

#include <stdio.h> 

#define BAR_STRUCT struct { int x; } 

typedef BAR_STRUCT bar; 

typedef struct { 
    union { 
     bar b; 
     BAR_STRUCT; 
    }; 
} foo; 

int main() { 
    foo f; 
    f.x = 989898; 
    printf("%d %d", f.b.x, f.x); 

    return 0; 
} 

匿名結構與一種應用廣泛,傳播推廣在C11之前的標準。

C++: 一樣C,你可以在這裏做,但匿名結構都沒有任何C++標準的一部分,而是一個擴展。 更好地使用繼承,或根本不使用這個快捷方式。

當然,不要使用類似#define x b.x))。

+0

我認爲這一定是我見過的過去。我記得很多關於結構對齊的問題。當然,'#define'是正確的。 – Stewart

+1

如果在union中有完全相同的'struct'定義,那麼不會有任何對齊問題。 – Yuki

+0

@Yuki,因此''define'做這樣的事情實際上是一個好主意*。 –