在 C 中向上转型和向下转型继承的结构

Upcasting and downcasting inherited structures in C

我想知道所提供的示例在 C:

中是否安全(无 UB)
typedef struct {
    uint32_t a;
} parent_t;

typedef struct {
    parent_t parent;
    uint32_t b;
} child_t;

typedef struct {
    uint32_t x;
} unrelated_t;

void test_function(parent_t* pParent) {
    ((child_t*)pParent)->b = 5U; // downcast is valid only if relation chain is valid
}

int main()
{
    child_t child;
    unrelated_t ub;
    test_function((parent_t*)&child); // valid upcast?
    test_function((parent_t*)&ub); // probably UB?

    return 0;
}

由于显式强制转换,没有保证和良好的类型检查,但只要传递了正确的参数,这应该可以正常工作吗?

铸造的规则https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

还有结构 https://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p15 :

A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa.

然后访问 https://port70.net/~nsz/c/c11/n1570.html#6.5p7 :

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:88)

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.

as long as proper parameters are passed this should work right?

“应该工作”和“保证工作”之间存在着巨大的海洋。目标是编写保证按预期工作的代码——它没有未定义的行为,因为无法保证未定义时会发生什么。

test_function((parent_t*)&child); // valid upcast?

是的,这是定义的行为。 &child 指向 &child.parent,因此结果指针必须正确对齐 parent_t

那么(child_t*)pParent也是有效的,因为它指向一个child_t对象。

然后访问它((child_t*)pParent)->也是有效的,因为有一个child_t访问的对象与它是相同类型child_t,所以它肯定是兼容类型。

test_function((parent_t*)&ub); // probably UB?

很可能,将指针unrelated_t*转换为parent_t*完全没问题——很可能alingof(unrelated_t)等于alingof(parent_t),因为里面只有uint32_t。但是,这取决于。当 (parent_t*)&ub 的结果指针未正确对齐到 parent_t.

时,它可能无效

之后,(child_t*)pParent 的类似故事在 test_function 中具有相同的指针值。请注意,child_t 可能比 parent_t 有更严格的对齐要求,因此虽然前面的转换可能有效,但这个可能不是,以相同的方式——取决于所涉及类型的对齐要求。

Then 在那之后,使用 ((child_t*)pParent)-> 访问 值是未定义的行为。通过 child_t * 句柄访问指向 unrelated_t 对象的指针是未定义的行为。 child_tunrelated_t 不兼容。