【问题标题】:Can I 'extend' a struct in C?我可以在 C 中“扩展”一个结构吗?
【发布时间】:2011-09-10 08:27:30
【问题描述】:
typedef struct foo_s {
    int a;
} foo;

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

基本上我想做:

bar b;
b.a;

我知道如果我在 bar 中命名了 foo 结构,我可以做 b.foo_name.a,但我不想这样做。

有什么办法吗?

这个问题得到了各种不同的答案,所以让我解释一下需求。我想这样做的原因是因为我有一个需要适应我的情况的库,这意味着我无法修改原始结构 decleration。此外,我需要做的就是在结构的开头添加 1 个项目(为什么是开头?因为我有一个“对象”结构,它负责项目中的所有结构)。我可以像你提到的那样简单地嵌入结构,但它真的很烦人,因为所有引用都需要输入'variable->image.location'那个'image'。键入十亿种类型真的很烦人。

【问题讨论】:

  • 一些编译器正好提供这种行为作为扩展。就我个人而言,我认为它提供了太大的风险(嵌套structs 六层深,忘记了你用过的名字)所以我只命名第一个成员_ - 简短、一致、不碍事,而且不是很不方便. (该名称实际上可能由标准和/或编译器保留,但我怀疑是否有人会使用它。)
  • 如果image 打字很烦人,请使用img。使用查找和替换来为您完成。别担心——无论你最终要做什么,你都会活下来。
  • @chacham15 伙计,避免将 C 问题标记为 C++。这两种是完全不同的语言。
  • 是的,我很快删除了标签
  • 结构名称不需要使用foo_stypedef struct foo { ... } foo; 工作正常。

标签: c struct


【解决方案1】:

显然是this feature has been added to C11,但可惜我无法使用最近的 C 编译器 (>= GCC 4.6.2)。

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;
}

【讨论】:

  • 请注意,这是有效的,因为在 bar 中,struct foo 是匿名使用的(即没有变量名)。
  • 如链接文档中所述,这是一个 Microsoft 扩展,在没有 -fms-extensions 标志的情况下无法在 GCC 和 clang 上编译。
【解决方案2】:

你可以使用指针,因为指向结构对象的指针保证指向它的第一个成员。参见例如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;
}

【讨论】:

  • 当他们采用void * 指针并避免调用方转换时会更好。 (有些人对“更好”的价值观可能与我不同。)
  • 是的,对调用者来说肯定更方便。我想证明 foo 函数的作者不必知道“真正的”结构不是 foo。
  • 嗯,这两种方法都是对的,也是错的。必须进行类型转换会迫使客户端代码质量低下。这可以通过使用void * 来解决,但是这会消除更糟糕的类型安全性。这些仍然是一个很好的答案和一些很好的论据,尤其是 Jouni 关于抽象(继承点)的最后评论,但需要考虑上述因素。这是 C11 解决方案:modelingwithdata.org/arch/00000113.htm;我想知道其他版本的 C 是否有解决方法
【解决方案3】:

C 中不可能像你那样做。但是您可以在bar 中模拟具有foo 成员变量的继承。

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

bar b;
b.obj.a = 10;

【讨论】:

  • 如果我没记错的话,那就是composition
  • 我认为这正是 OP 想要的;这是“明显的解决方案”。
【解决方案4】:

如果你想

typedef struct foo_s {
    int a;
  } foo;

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

所以你可以这样做:

bar b; b.my_foo.a = 3;

否则,由于sizeof(bar_s) 不利于编译时间,因此无法在 C 中执行此操作。这不是一个好习惯,但您可以在 bar_s 中保存一个 void * ptr; 指针,以及另一个描述 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;
 }

【讨论】:

  • 这个问题没有提到想要你描述的运行时“类型”。
  • @Chris,你可能是对的,但“bar.a”对我来说就像一个 python 代码(可以在运行时扩展类)。所以这就是我首先想到的......
  • 在我看来,它更像是普通的旧 C++ 继承,而且他的代码是启用了扩展的某些编译器的有效代码。除非foo 是不完整的类型,否则sizeof(bar) 不会有问题。 @pst - 不是真的。问题是问“我知道我可以做 X,但 Y 会更方便。我可以这样做吗?”解释 Z 并没有那么重要。
  • @Chris,正如我之前所说,乍一看我可能误解了这个问题。我试图解释为什么 C 不支持不完整类型(编译器需要对象的大小)
【解决方案5】:

可以通过预处理器轻松完成:

创建一个名为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;

【讨论】:

  • 不需要创建额外的文件,你可以在多行中定义结构字段
  • 这个解决方案还是有点丑。
  • @U.Windl 是的,您也可以只编写一个快速脚本生成“正确”结构,它们只是共享相同的内存布局......不会超过几行用于生成所有扩展 C 结构的 JavaScript。
【解决方案6】:

您可以尝试使用继承:

struct foo_s
{
    int a;
};

struct bar_s: foo_a
{
    int b;
};

在 C++ 中工作,不确定它是否在 C 中工作。

【讨论】:

  • 我的错,你是对的,我不知道为什么我添加了 c++ 标签,我的意思是 c。对不起!
  • 继承在 C AFAIK 中不起作用。我怀疑 OP 正是想在 C 中模仿 C++ 的继承机制。
【解决方案7】:

匿名结构和具有无名字段的联合之间存在混淆。无名字段是 Microsoft 扩展。

struct known {
    struct /* anonymous */ {
        int anonymous;
    };
    int known;
};

匿名structunion 是一个structunion,没有嵌入另一个structunion 中的任何tag name。它也不需要有任何字段名称。

无名字段是允许在 C 中进行有限继承的 Microsoft 扩展。

struct A {
    int a;
};

struct B {
    struct A: // nameless field
    int b;
};

匿名 structunion 不是无名字段,无名字段不是匿名的,至少 C11 标准定义它的方式。

【讨论】:

    【解决方案8】:

    这是没有 c 标志的最简单方法

    #include <stdio.h>
    
    #define foo_s struct { int a; }
    typedef foo_s foo;
    
    typedef struct bar_s {
        foo_s; // extends foo_s
        int b;
    } bar;
    
    int main(void)
    {
        bar b = {
            .a = 1,
            .b = 2,
        };
        foo *f = (foo *)&b;
    
        printf("a: %d\n", f->a);
    
        return 0;
    }
    
    $ gcc inherit.c
    $ ./a.out
    a: 1
    

    【讨论】:

    • 但是你不是在bar_s中使用匿名结构foo_s吗?
    • @U.Windl foo_s in bar_sfoo 完全相同。你可以用foo *指向foo_s
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-08
    • 2011-03-31
    相关资源
    最近更新 更多