扩展结构

时间:2014-05-23 08:11:22

标签: c struct

typedef struct A 
{
  int x;
}A;

typedef struct B 
{
  A a;
  int d;
}B;

void fn()
{
  B *b;
  ((A*)b)->x = 10;
}

我在SO中阅读了上面的代码片段。 ((A*)b)->x不是很好的编程风格。b->a.x是很好的编程风格。因为任何人在声明之前添加了一些内容" A a;"在结构b中,它将无法工作。我不明白为什么?我也尝试过了。有什么建议吗?

2 个答案:

答案 0 :(得分:4)

该技巧用于模拟C中的继承。它可以将地址A或B传递给期望指向A的函数。

这是有效的,因为C保证在struct的第一个成员之前没有填充。因此,如果A是B的第一个成员,则B开头的内存布局始终与A相同。

int doStuff(A * a) {
    return a->x + 1;
}

...

B b;
doStuff((A*)&b); // Will work because b and b.a have the same start address

如果您要更改B声明:

typedef struct B 
{
    int d;
    A a;
}B;

这将不再有效,因为(A*)&b将返回b.d的地址,而不是b.a

答案 1 :(得分:1)

你在这里所拥有的是一个穷人的遗产"。与C ++中的真正继承一样,它用于定义包含对象的公共特征(数据,函数指针)的类型,这些对象实际上可以携带比公共子集更多的信息。

该技术广泛用于例如GhostScript,其中打印机驱动程序包含一些常用信息和最重要的特殊信息,以控制特定的打印机型号。

这里使用的C语言机制是结构本质上是其内存中数据的串联,按成员声明的顺序。该顺序对于在转换后立即获取访问权限非常重要。 B的内存布局为|---int x---|---int d ---|。没有存储其他信息。 A *指向第一个元素x; B *也是如此。你可以有一个struct c

struct C 
{
   B b; 
   float f; 
};

其布局为|---int x---|---int d ---|-----float f---|。有趣的是,你可以将一个A *pa传递给一个函数,该函数以某种方式知道pa实际指向一个C并且向下投射" down":((C *)pa)->f(C *)pa不会改变pa的值,只是告诉编译器它指向的是什么(由程序员负责)。有关实际类型隐藏在对象中的知识通常编码在enum / int数据成员中,该成员在创建对象时手动设置为魔术类型指示值。