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

aki*_*ata 1 c language-lawyer

我想知道所提供的示例在 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;
}
Run Code Online (Sandbox Code Playgroud)

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

Kam*_*Cuk 5

投射规则为https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7

\n
\n

指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。

\n
\n

同样在结构的情况下https://port70.net/~nsz/c/c11/n1570.html#6.7.2.1p15

\n
\n

指向结构对象的指针经过适当转换后,指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。

\n
\n

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

\n
\n

对象的存储值只能由具有以下类型之一的左值\n表达式访问:88)

\n
    \n
  • 与对象的有效类型兼容的类型,
  • \n
  • 与对象的有效类型兼容的类型的限定版本,
  • \n
  • 与对象的有效类型相对应的有符号或无符号类型,
  • \n
  • 与对象有效类型的限定版本相对应的有符号或无符号类型的类型,
  • \n
  • 在其成员中包含上述类型之一的聚合或联合类型(递归地包括子聚合或包含的联合的\n成员),或者
  • \n
  • 一种字符类型。
  • \n
\n
\n
\n
\n

只要传递正确的参数,这应该可以正常工作吗?

\n
\n

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

\n
\n
test_function((parent_t*)&child); // valid upcast?\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n

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

\n

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

\n
\n
test_function((parent_t*)&ub); // probably UB?\n
Run Code Online (Sandbox Code Playgroud)\n
\n

最有可能的是,从to转换指针是完全可以的 \xe2\x80\x93 最有可能等于,因为只有里面。但是,这取决于。当 的结果指针未正确对齐时,它可能无效unrelated_t*parent_t*alingof(unrelated_t)alingof(parent_t)uint32_t(parent_t*)&ubparent_t

\n

之后,类似的故事,(child_t*)pParent内部具有相同的指针值test_function。请注意,child_t可能有更严格的对齐要求parent_t,因此虽然前一个转换可能有效,但这个可能无效,以同样的方式 \xe2\x80\x93 取决于所涉及类型的对齐要求。

\n

之后,访问该值((child_t*)pParent)->是未定义的行为。unrelated_t通过句柄访问指向对象的指针child_t *是未定义的行为。child_t与以下类型不兼容unrelated_t

\n