gpt4 book ai didi

c++ - 如果 Derived 不向 Base 添加新成员(并且是 POD),那么可以安全地完成什么样的指针转换和取消引用?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:37:59 27 4
gpt4 key购买 nike

(这是关于未定义行为 (UB) 的另一个问题。如果这段代码在某些编译器上“有效”,那么这在 UB 领域就没有任何意义。这是可以理解的。但是我们到底在下面的哪一行跨入 UB?)

(关于 SO 已经有很多非常相似的问题,例如 (1) 但我很好奇在取消引用指针之前可以安全地使用指针做什么。)

从一个非常简单的基类开始。没有 virtual方法。无继承。 (也许这可以扩展到任何 POD?)

struct Base {
int first;
double second;
};

然后是添加(非 virtual)方法但不添加任何成员的简单扩展。没有 virtual继承。

struct Derived : public Base {
int foo() { return first; }
int bar() { return second; }
};

然后,考虑以下几行。如果与定义的行为有一些偏差,我很想知道到底是哪一行。我的猜测是我们可以安全地对指针执行大部分计算。这些指针计算中的一些,如果没有完全定义,是否有可能至少给我们某种并非完全无用的“不确定/未指定/实现定义”的值?

void foo () {
Base b;
void * vp = &b; // (1) Defined behaviour?
cout << vp << endl; // (2) I hope this isn't a 'trap value'
cout << &b << endl; // (3a) Prints the same as the last line?
// (3b) It has the 'same value' in some sense?
Derived *dp = (Derived*)(vp);
// (4) Maybe this is an 'indeterminate value',
// but not fully UB?
cout << dp << endl; // (5) Defined behaviour also? Should print the same value as &b

编辑:如果节目到这里结束,会是UB吗?请注意,在这个阶段,我没有尝试对 dp 做任何事情。 ,而不是将指针本身打印到输出。如果简单的转换是 UB,那么我想问题到此结束。

                        // I hope the dp pointer still has a value,
// even if we can't dereference it
if(dp == &b) { // (6) True?
cout << "They have the same value. (Whatever that means!)" << endl;
}

cout << &(b.second) << endl; (7) this is definitely OK
cout << &(dp->second) << endl; // (8) Just taking the address. Is this OK?
if( &(dp->second) == &(b.second) ) { // (9) True?
cout << "The members are stored in the same place?" << endl;
}
}

我对上面的(4) 有点紧张。但我认为转换到 void 指针和从 void 指针转换总是安全的。也许可以讨论这样一个指针的。但是,它是否被定义为进行转换,并打印指向 cout 的指针? ?

(6) 也很重要。这会评估为真吗?

(8) 中,我们第一次取消引用此指针(正确的术语?)。但请注意,这一行不是从 dp->second 读取的。 .它仍然只是一个左值,我们获取它的地址。我假设这种地址计算是由我们从 C 语言获得的简单指针算术规则定义的?

如果以上都OK,或许我们可以证明static_cast<Derived&>(b)没问题,并且会导致一个完美可用的对象。

最佳答案

  1. 从数据指针转换为 void *始终保证工作,并且指针保证在往返过程中存活 Base * -> void * -> Base * (C++11 §5.2.9 ¶13);
  2. vp是一个有效的指针,所以应该没有任何问题。
  3. 一个。尽管打印指针是实现定义的1,打印的值应该是相同的:事实上operator<<默认情况下仅为 const void * 重载, 所以当你写 cout<<&b您正在转换为 const void *无论如何,即什么 operator<<在这两种情况下都看到了 &b转换到 const void * .

    是的,如果我们采用“具有相同值”的唯一合理定义 - 即它与 == 比较相等运算符(operator);事实上,如果你比较vp&b== , 结果是 true , 如果你转换 vpBase * (由于我们在 1 中所说的),如果你转换 &bvoid * .

    这两个结论都来自 §4.10 ¶2,其中指定任何指针都可以转换为 void * (取模通常的 cv 限定的东西),结果«指向对象 [...] 所在的存储位置的开始»1

  4. 这很棘手; C 风格的转换相当于 static_cast , 这将很高兴地允许将 «"pointer to cv1 B [...] 转换到 [...] "pointer to *cv2 D ",其中 D 是从 B 派生的类 »(§5.2.9,¶11;有一些额外的约束,但它们在这里得到满足);但是:

    If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.

    (强调)

    因此,这里允许您进行强制转换,但结果未定义...

  5. ... 引导我们打印它的值;由于转换的结果未定义,您可能会得到任何东西。由于指针可能被允许具有陷阱表示(至少在 C99 中,我只能在 C++11 标准中找到对“陷阱”的稀疏引用,但我认为这种行为可能应该已经从 C89 继承)你甚至可以只需读取 这个指针并通过 operator<< 打印它就会崩溃.

如果遵循,则 6、8 和 9 也没有意义,因为您使用的是未定义的结果。

此外,即使转换有效,严格的别名(§3.10,¶10)也会阻止你对指向的对象做任何有意义的事情,因为别名是 Base通过 Derived 对象仅当 Base 的动态类型时才允许使用指针对象实际上是 Derived ;任何偏离 §3.10 ¶10 中指定的异常的行为都会导致未定义的行为。


注释:

  1. operator>>代表num_put在概念上委托(delegate)给 printf%p ,其描述归结为“定义的实现”。

  2. 这排除了我的担心,即在转换为 void * 时,邪恶的实现在理论上可能会返回不同但等效的值。 .

关于c++ - 如果 Derived 不向 Base 添加新成员(并且是 POD),那么可以安全地完成什么样的指针转换和取消引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19741843/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com