2011-09-10 49 views
10
typedef struct foo_s { 
    int a; 
} foo; 

typedef struct bar_s { 
    foo; 
    int b; 
} bar; 

基本上我想做的事:我可以在C中擴展一個結構嗎?

bar b; 
b.a; 

我知道我能做到b.foo_name.a如果我命名的酒吧foo的結構,但編號不喜歡。

任何方式來做到這一點?

這個問題已經得到了各種不同的答案,所以讓我解釋一下需求。我想這樣做的原因是因爲我有一個庫,我需要適應我的情況,這意味着我不能修改原始結構體的刪除。此外,我需要做的就是在結構的開頭添加1項(爲什麼開始?因爲我有一個'object'結構,它負責項目中的所有結構)。我可以簡單地嵌入像你提到的結構,但它真的很煩人,因爲所有的引用都需要鍵入'variable-> image.location'這個'image'。鍵入十億種類型真的很煩人。

+0

一些編譯器提供的正是這樣的行爲一個延伸。就我個人而言,我認爲它提供了太多的風險(嵌套'結構'六層深,忘記了你使用的名稱),所以我只是將第一個成員'_'命名 - 簡短,一致,不會很不方便。 (這個名字可能實際上是由標準和/或編譯器保留的,但是我懷疑如果是的話,任何人都會使用它。) –

+0

如果'image'很煩,請使用'img'。使用查找和替換爲你做。不要擔心 - 無論你最終需要做什麼,你都會活下來。 –

+0

@ chacham15老兄,避免將C問題標記爲C++。這兩個是完全不同的語言。 –

回答

19

顯然this feature has been added to C11,但可惜我沒有訪問最近的復古(> = 4.6.2 GCC)的C編譯器。

typedef struct foo { 
    int a; 
} foo; 

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

int main() { 
    bar b; 
    b.a = 42; 
    b.b = 99; 
    return 0; 
} 
+1

請注意,這是因爲在'bar'內,'struct foo'是匿名使用的(即沒有變量名稱)。 –

7

不可能在C這樣做。但是您可以在bar中模仿foo成員變量的繼承。

typedef struct bar_s { 
    foo obj; 
    int b; 
} bar; 

bar b; 
b.obj.a = 10; 
+5

如果我沒有弄錯,那就叫做[組合](http://en.wikipedia.org/wiki/Composition_over_inheritance)。 – michaelsnowden

1

您可以嘗試使用繼承:

struct foo_s 
{ 
    int a; 
}; 

struct bar_s: foo_a 
{ 
    int b; 
}; 

作品在C++中,不知道它在C.

+1

我不好,你是對的,我不知道爲什麼我加了C++標籤,我的意思是c。 SRY! – chacham15

+4

我敢肯定,這不是C –

+0

最好的答案,即使是它的C++。 – lama12345

2

如果您換貨

typedef struct foo_s { 
    int a; 
    } foo; 

typedef struct bar_s { 
foo my_foo; 
int b; 
} bar; 

所以你可以做:

bar b; b.my_foo.a = 3; 

否則,在C中沒有辦法做到這一點,因爲sizeof(bar_s)在編譯時是不利的。這不是一個好的做法,但是您可以將一個void * ptr;指針保存在bar_s中,另一個枚舉描述ptr類型,並按類型進行類型轉換。

即:

typedef enum internalType{ 
    INTERNAL_TYPE_FOO = 0, 
}internalType_t; 

typedef struct bar_s { 
internalType_t ptrType; 
void* ptr; 
int b; 
} bar; 

然後:

bar b; foo f; 
b.ptrType = INTERNAL_TYPE_FOO; 
b.ptr = &f; 

和一些別的地方的代碼:

if (b.ptrType == INTERNAL_TYPE_FOO) { 
    foo* myFooPtr = (foo *)b.ptr; 
} 
+0

這個問題沒有提到像你描述的那樣需要運行時「類型」。 –

+0

@Chris Lutz也許是過度的,但它確實解決了這個問題... – 2011-09-10 08:54:37

+0

@Chris,你可能是對的,但是「bar.a」看起來像一個python代碼給我(其中類可以在運行時擴展) 。所以這就是我想到的第一個... –

9

您可以使用指針,因爲一個指向結構對象保證指向其第一個成員。見例如this article

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

typedef struct foo_s { 
    int a; 
} foo; 

typedef struct bar_s { 
    foo super; 
    int b; 
} bar; 

int fooGetA(foo *x) { 
    return x->a; 
} 

void fooSetA(foo *x, int a) { 
    x->a = a; 
} 

int main() { 
    bar* derived = (bar*) calloc(1, sizeof(bar)); 
    fooSetA((foo*) derived, 5); 
    derived->b = 3; 
    printf("result: %d\n", fooGetA((foo*) derived)); 
    return 0; 
} 
+1

當他們使用'void *'指針並避免調用者端投射時更好。 (有些人的「更好」的價值可能與我的不同。) –

+1

是的,這對於來電者來說當然更方便。我想證明foo函數的作者不必知道「真正的」結構是foo以外的東西。 –

+1

那麼,這兩種方法是同時對錯的。必須強制性強制客戶端代碼的質量較低。這可以通過使用'void *'來解決,但是這會消除更糟糕的類型安全性。儘管如此,這仍然是一個很好的答案和一些很好的論點,特別是Jouni在抽象(繼承點)方面的最後評論,但上述因素需要考慮。以下是C11解決方案:** http://modelingwithdata.org/arch/00000113.htm**;我想知道是否有其他版本的解決方法C – Powerslave

0

它可以通過預處理器可以輕鬆完成:

創建一個名爲base_foo.h文件:

int foo; 

然後只需包含它:

typedef struct foo_s { 
    #include "base_foo.h" 
} foo; 

typedef struct bar_s { 
    #include "base_foo.h" 
    int b; 
} bar; 
相關問題