2014-05-23 83 views
1
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中,它将不起作用。我不明白为什么?我也尝试过。有什么建议吗?扩展结构

+0

为什么'b'被转换为'A *'?这两种类型似乎是无关的,除了'B'具有'A'类型的成员。据我的理解,赋值'((A *)b) - > x = 10'将取决于具体的内存布局。 – Codor

+0

b-> a.x很容易理解。但另一种格式对我来说并不容易理解。 –

+0

如果我没有弄错,那么这两项任务在结构上完成不同的事情,并且最好偶然地得到相同的结果。 – Codor

回答

4

即特技被用来模拟在继承C.它使得可能通过地址A或B的函数会预计指针A.

这适用,因为C保证有第一构件之前没有填充结构。因此,如果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

+0

非常有趣,我完全没有意识到在C中实现继承的可能性。非常感谢! – Codor

+0

虽然警告的话:这仍然是一个伎俩,它很容易难以维护。如果可能,我真的推荐使用支持继承的语言作为核心功能。 – user694733

+0

这里描述的案例(由OP提出)没有那么有趣,因为(正如OP陈述的)B带有关于包含A的知识,所以b.a.x确实是可能的正确途径。相比之下,处理指向Bs和访问B的成员d的A *是一个有趣的用例。在声明OP所要求的“派生”结构时,它需要与成员顺序相同的规则。 –

1

你这里有什么是“穷人的遗产”。像C++中的真正继承一样,它用于定义一个类型,它包含对象的共同特征(数据,函数指针),事实上这些对象可以携带比普通子集更多的信息。

该技术被广泛用于例如GhostScript中的打印机驱动程序携带一些常用信息以及用于控制该特定打印机型号的特殊信息。

这里使用的C语言机制是一个结构本质上是按照成员声明的顺序在内存中连接它的数据,该订单对于铸造后访问权限很重要。 你B的内存布局是|---int x---|---int d ---|。没有额外的信息存储。 A *指向第一个元素x; B *也是如此。你可以有一个结构c

struct C 
{ 
    B b; 
    float f; 
}; 

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

+0

只是挑剔:'(C *)pa'被称为downcast,而不是upcast。请参阅:http://programmers.stackexchange.com/questions/148615/what-is-upcasting-downcasting –

+0

还有一个疑问。声明A a;在结构b转换成结构A的成员? –

+0

@ user3168736它的确如此,就是说内存不包含A的成员数据。特别是内存中不包含任何可以将其标识为属于A的内容 - 该信息仅在编译时从声明中可用。在编译时,编译器会强制只有通过a才能访问a的成员,也就是说你必须写bax并且不能写bx(除了匿名结构:struct O {struct {int ii;};} o; int i = o ';但我没有看到这个好处,除了生成的代码)。 –