【问题标题】:Upcasting and downcasting inherited structures in C在 C 中向上转换和向下转换继承的结构
【发布时间】:2021-12-28 07:19:22
【问题描述】:

我想知道提供的示例在 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;
}

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

【问题讨论】:

  • "...这应该可行。" 什么应该可行?如果您的意思是保证childub 在内存中的相对位置 - 没有。
  • @WeatherVane 我想他们是在问parent 是否保证在child 的偏移量为0,以及指针在两个方向上的转换是否有效。
  • @akimata 当你说“应该工作”时,打算发生什么?请使用输出显示预期结果。 C语言中的“继承、向上转换和向下转换”是什么意思?
  • @HolyBlackCat - 正是我所要求的,如果我可以安全地在孩子体内上下投掷。 Ub 只是编译器不会抱怨的一个例子,但行为很确定是未定义的,我不确定是否有更清洁/更安全的解决方案来捕获此类错误。
  • 你知道unions吗?

标签: c language-lawyer


【解决方案1】:

转换规则https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7

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

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

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

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

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

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

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

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

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 有更严格的对齐要求,所以虽然前面的转换可能有效,但这次可能不是,以同样的方式 - 取决于所涉及类型的对齐要求。

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

【讨论】:

  • 好的,如果结构指针像 parent_t 和 child_t 一样相互关联,向上转换和向下转换是安全的。
  • 这真的无关...我想传达信息:只要对齐没问题,进行任何演员都是安全的。你可以做(uint16_t*)&ub。仅使用兼容句柄访问 数据是安全的。在这种情况下,指向结构的指针指向它的初始成员,因此两种访问都可以。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-24
  • 2018-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多