C,结构指针多态性

blo*_*low 0 c polymorphism inheritance

注意:这不是C++问题,我不能使用C++编译器,只能使用C99.

这是有效的(可接受的,漂亮的)代码吗?

typedef struct sA{
   int a;
} A;

typedef struct aB{
   struct sA a;
   int b;
} B;

A aaa;
B bbb;

void init(){
   bbb.b=10;
   bbb.a.a=20;
   set((A*)&bbb);
}

void set(A* a){
   aaa=*a;
}

void useLikeB(){
   printf("B.b = %d", ((B*)&aaa)->b);
}
Run Code Online (Sandbox Code Playgroud)

简而言之,当我需要指定的行为时,将"子类"转换为"超类"并重铸后的"超类"到"子类"是有效的吗?

谢谢

Mic*_*rny 5

首先,C99标准允许您将任何结构指针强制转换为指向其第一个成员的指针,另一种方式(6.7.2.1结构和联合说明符):

13在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头.

换句话说,在您的代码中,您可以自由地:

  1. 转换B*A*- 它将始终正常工作,
  2. 转换A*B*- 但是如果它实际上没有指向B,那么您将获得访问其他成员的随机失败,
  3. 分配结构指出通过A*A-但如果指针是从转换B*,只有公共元件将被分配和的其余成员B都将被忽略,
  4. 分配结构,指出通过B*A-但你必须指针先转换方法及注意事项(3).

所以,你的例子几乎是正确的.但是useLikeB()不能正常工作,因为你指定aaa的类型的结构A如第(4)节所述.这有两个结果:

  1. 非共同B成员实际上不会被复制aaa(如(3)所述),
  2. 你的程序将无法随意尝试访问A一样B,它不是(你所访问的成员是不存在的,如(2)).

要以更实际的方式解释这一点,当您声明A编译器保留保存所有成员所需的内存量时A.B有更多的成员,因此需要更多的记忆.作为A常规变量,它不能在运行时更改其大小,因此无法保留其余成员B.


作为一个注释,通过(1)你几乎可以指向成员而不是转换更好的指针,它将允许你访问任何成员,而不仅仅是第一个成员.但请注意,在这种情况下,相反的情况将不再适用!