【问题标题】:Is it undefined behavior in C++ to dereference an invalid pointer but not use the result?取消引用无效指针但不使用结果是否是 C++ 中未定义的行为?
【发布时间】:2025-11-26 22:05:03
【问题描述】:

考虑以下一段 C++ 代码:

int main() {
    int* ptr; // Deliberately uninitialized
    (void) *ptr;     // Dereference, do not use result
}

此代码是否会导致未定义的行为?我假设答案是“是”,即使 *ptr 的值实际上并未在任何地方使用。规范中是否有特定部分可以保证这一点?

(如果这是重复的,我深表歉意,但我似乎在网站上找不到任何其他专门提出这个问题的内容。)

【问题讨论】:

  • 至少未定义的行为不受 as-if 规则的约束。 :)
  • 你为什么在乎?为什么要在不使用值的情况下取消指针?
  • If a tree falls in a forest 哲学难题的 C++ 等价物。
  • 如果委员会花时间回答“如果取消引用无效指针但结果被丢弃会发生什么”的问题,我会非常失望。俗话说“这就是为什么我们可以没有好东西”
  • 请注意 (*.com/questions/4285895/…) 中的讨论,由 @MarinosK 提出

标签: c++ pointers language-lawyer


【解决方案1】:

简单地取消引用尚未分配或标记为只读的指针可能会导致CPUs 内存管理单元中的硬件异常。因此,即使您不使用通过取消引用包含随机值的指针来返回的任何垃圾值,也不能保证 CPU 会从此类事件中返回。

但是,根据ISO C++11 Standard,声明为未初始化的指针必须具有奇异值。所以一个未初始化的指针的值不是 undefined 因为它不是垃圾。

然而标准规定,对这种奇异值指针的大多数操作都是未定义的,除了用非奇异值覆盖奇异值 em>:

24.2.1 一般而言 [ iterator.requirements.general ]

5 [ 示例:在声明未初始化的指针 x 之后(与 int* x; 一样),必须始终假定 x有一个指针的奇异值。 — end example ] 大多数表达式的结果对于奇异值是未定义的;唯一的例外是销毁包含奇异值的迭代器,将非奇异值分配给包含奇异值的迭代器,并且对于满足 DefaultConstructible 要求的迭代器,使用值初始化的迭代器作为复制或移动操作。 [ 注意:此保证不适用于默认初始化,尽管该区别仅对具有琐碎默认构造函数的类型(例如指针或持有指针的聚合)有意义。 — 尾注 ] 在这些情况下,奇异值会像任何其他值一样被覆盖。可取消引用的值总是非单数的。

【讨论】:

    【解决方案2】:

    您正在读取未初始化变量(指针)的值。那是未定义的行为。

    有趣的是,因为*ptr 是一个废弃的值表达式,它不会进行左值到右值的转换。因此,如果*ptr 未初始化,它将是安全的。

    【讨论】:

    • 我强烈怀疑你是对的。不过,鉴于我添加的 [language-lawyer] 标签,我有点希望从支持这一点的规范中获得一些措辞。
    • @templatetypedef 当然,我强烈同意 Galik 的回答被接受。
    【解决方案3】:

    根据标准,取消引用未初始化的指针是未定义的行为。然而,在现实生活中这种情况很少发生:编译器足够聪明,可以删除这样的代码,因为它实际上并没有在任何地方使用。所以即使取消引用指针是未定义的行为,如果你不使用结果,机会是你实际上并没有取消引用它。我用 clang++ 测试了你的代码,没有生成任何代码。

    来自标准(4.1):

    可以转换非函数、非数组类型 T 的左值 (3.10) 为右值。如果 T 是不完全类型,则程序需要 这种转换是不正确的。如果左值指向的对象 refer 不是类型 T 的对象,也不是类型的对象 从 T 派生,或者如果对象未初始化,则程序 需要此转换具有未定义的行为。如果 T 是一个 非类类型,右值的类型是 cv-unqualified 版本 的T。否则,右值的类型是T。

    【讨论】:

    • 请引用任何相关参考资料。
    • 如果标准中未明确定义某些内容,则根据定义未定义,并且没有定义用于取消引用初始化变量的行为。这在 4.1 中也很明确:*.com/questions/4285895/…
    • 嗯,我不确定那个论点的有效性。但是,如果您引用支持该论点的标准的相关部分,那仍然会很好。当然,还有论点本身。
    • 我已经引用了上面的相关部分
    【解决方案4】:

    根据标准,这不是未定义的行为。

    这里是来自the open-std review page的官方讨论和解释。

    我们同意标准中的方法似乎没问题:p = 0; *p;本质上不是错误。左值到右值的转换会给它带来不确定的行为。

    另外在标准 [第 4.10.2 节] 中,它说:

    “指向 cv T 的指针”类型的纯右值,其中 T 是对象类型,可以转换为“指针”类型的纯右值 CV 无效”。将指向对象类型的指针的非空指针值转换为“指向对象的指针”的结果 cv void”表示与原始指针值相同的字节在内存中的地址。空指针 value 被转换为目标类型的空指针值。

    【讨论】:

    • 我认为这是一个很好的答案,但我赞成,因为顶部的断言是没有根据的。这是一个共识,也是该标准进一步修订的目标。我不认为这是事实(还)。
    • 提问者没有空指针,而是一个未初始化的指针。看起来这个警告仅适用于空指针,但不适用于随机内存地址。
    • 指针正在从 int* 转换为 void 在取消引用中,这是我所指的部分。
    • −1 Re“这与给出的示例几乎相同。”,不,那是联想思维。转换为void,一个丢弃的表达式,与转换为void* 有很大的不同。
    • @Cheersandhth.-Alf,明白了。我会改变的。
    最近更新 更多