0

注意:这不是 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);
}

简而言之,当我需要指定行为时,将“子类”转换为“超类”并在将“超类”重新转换为“子类”之后是否有效?

谢谢

4

4 回答 4

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. 您的程序将随机尝试访问它不存在的方式失败(您正在访问一个不存在的成员,如 (2) 中所述)AB

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


请注意,通过(1),您实际上可以获取指向成员的指针,而不是转换更好的指针,并且它将允许您访问任何成员,而不仅仅是第一个成员。但请注意,在这种情况下,相反的方法将不再起作用!

于 2012-08-18T14:48:43.087 回答
2

我认为这是相当肮脏和相对危险的。你想用这个来达到什么目的?也不能保证 aaa 是 B ,它也可能是 A。所以当有人调用“uselikeB”时它可能会失败。还取决于架构“int a”和“指向结构 a 的指针”可能会正确或不重叠,并且当您分配给“int a”然后访问“struct a”时可能会导致有趣的事情发生

于 2012-08-18T13:30:19.593 回答
0

你为什么要这样做?有

set((A*)&bbb);

写起来并不比正确更容易

set(&bbb.a);

您在此处发帖时应避免的其他事项:

  • set在声明之前使用
  • aaa=a应该aaa = *a
于 2012-08-18T14:37:45.213 回答
0

首先,我同意之前发帖人对这项作业的安全性的大多数担忧。

话虽如此,如果您需要走那条路,我会添加一个间接级别和一些类型安全检查器。

static const int struct_a_id = 1;
static const int struct_b_id = 2;
struct MyStructPtr {
   int type;
   union {
     A* ptra;
     B* ptrb;
     //continue if you have more types.
   }
};

这个想法是您通过将指针传递给包含一些“类型”信息的结构来管理指针。您可以在描述您的类树的一侧构建一个类树(请注意,鉴于安全转换的限制,这可以使用树来表示)并且能够回答问题以确保您正确地上下转换结构。所以你的“useLikeB”函数可以这样写。

MyStructPtr the_ptr;
void init_ptr(A* pa)
{
     the_ptr.type = struct_a_id 
     the_ptr.ptra = pa;
}
void useLikeB(){
    //This function should FAIL IF aaa CANT BE SAFELY CASTED TO B
    //by checking in your type tree that the a type is below the 
    //a type (not necesarily a direct children).
    assert( is_castable_to(the_ptr.type,struct_b_id ) ); 
    printf("B.b = %d", the_ptr.ptrb->b);
}

我的 2 美分。

于 2012-08-18T15:15:30.547 回答