【问题标题】:Is it UB to cast away const and read value? [duplicate]是 UB 抛弃 const 并读取值吗? [复制]
【发布时间】:2015-02-18 01:42:04
【问题描述】:

澄清:我的问题是:

  • 使用int类型的左值访问有效类型const int的对象是UB吗?

这个问题有两个代码示例,它们使用 int 类型的左值来访问有效类型 const int 的对象,我的目的是尽可能少地分心来实现这一点。如果除此特定问题之外还有其他任何 UB 来源,请发表评论,我将尝试更新代码示例。


下面是具体的代码示例供大家讨论:

#include <stdio.h>
#include <stdlib.h>

int main()
{
    const int c = 5;

    printf("%d\n", *(int *)&c);
}

我认为可能是UB的原因是严格的别名规则似乎说它是UB:

C11 6.5/7

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

  • 与对象的有效类型兼容的类型,
  • 与对象的有效类型兼容的类型的限定版本,
  • ...

对象的有效类型(6.5/6)这里是const int

第一个要点:intconst int 不是兼容类型(6.2.7/1、6.7.3/10)。

第二个要点:int 似乎不是const int 的合格版本,因为我们没有通过添加限定符来生成它。但是 6.2.5/26 不清楚:

每个非限定类型都有其类型的多个限定版本,对应于 const、volatile 和 restrict 限定符中的一个、两个或所有三个的组合。类型的合格或不合格版本是属于同一类型类别并具有相同表示和对齐要求的不同类型。派生类型不受其派生类型的限定符(如果有)限定。

它没有定义“const int 的合格版本”是什么,它只定义了应用于不合格类型时的术语“合格版本”。


第二个代码示例:

int *pc = malloc(sizeof *pc);
memcpy(pc, &c, sizeof c);
printf("%d\n", *pc);   // UB?

由于 memcpy 保留有效类型 (6.5/6) ,读取 *pc 与严格别名规则的交互与在第一个示例中读取 *(int *)&amp;c 完全相同。

【问题讨论】:

  • 为什么你需要 un-const 一个你只能读取的变量?
  • @StenSoft 我不需要需要,但知道这是否是 UB 会很有趣,并且在其他问题中也有相同的原则(例如推论我刚刚添加)
  • 我可以肯定地告诉你,这在实践中可能行不通(在小型 POD 上抛弃 const)。当 GCC 将一个 const 字符放入寄存器时,我被它咬了一口,然后我厌倦了修改它:) 它导致了 SGIBUSSIGTERM (它发生已经有几年了)。弄乱 const-ness 后果自负 :)
  • @jww 修改它无疑是UB,但如果你只阅读它呢?
  • @user3386109 我想知道它是否违反了任何规则。我已经通过引用严格别名规则作为开始,因为它可能是候选规则,但我不想将讨论仅限于严格别名规则。在这个阶段,我认为这不值得提出两个单独的问题(1. 是否适用严格的别名规则,2. 是否有任何其他问题)因为我没有理由相信除了结构别名之外还有任何其他问题——但我可能是错的,我想让它开放给其他人提交任何东西。同意我们应该删除这些 cmets

标签: c language-lawyer strict-aliasing


【解决方案1】:

事实并非如此。您发现的是为什么它不能被隐式转换。

[6.2.5/26] 状态:

每个非限定类型都有其类型的多个限定版本,对应于 const、volatile 和 restrict 限定符中的一个、两个或所有三个的组合。 类型的合格或不合格版本是属于同一类型类别并具有相同表示和对齐要求的不同类型。

(注意:每个不合格类型。const int 不是不合格,但int 是不合格。)

带脚注:

相同的表示和对齐要求意味着作为函数的参数、函数的返回值和联合成员的可互换性。

这意味着读取它将以相同的方式工作并产生相同的值。

[6.7.3/6] 指定 UB 仅用于修改:

如果尝试通过使用具有非 const 限定类型的左值来修改使用 const 限定类型定义的对象,则行为未定义。

【讨论】:

  • 脚注是非规范性的,但无论如何,我们不是在谈论函数的参数、函数的返回值或联合成员,所以我看不出它是如何应用的,或者你如何从中得出结论,读取必须是可以的。您如何看待相同的问题,但使用 volatile 而不是 const
  • 它具有相同的表示和对齐要求,因此您应该能够以相同的方式阅读它。脚注有助于理解其背后的原因。对于 volatile,[6.7.3/6] 指定任何对 volatile 的非易失性引用都是 UB。
  • 很好地发现了 6.7.3/6 中明确禁止 volatile ,我已从我的问题中删除了该段
  • 你是说严格的别名规则有缺陷的文本,它还应该包括通过“任何具有相同表示和对齐要求的东西”来阅读?
  • 它确实违反了严格的别名规则。但是别名只有在值发生变化时才会触发 UB(重新排序读取是安全的),这无论如何都会导致 UB。
猜你喜欢
  • 2012-09-28
  • 1970-01-01
  • 2022-11-15
  • 1970-01-01
  • 1970-01-01
  • 2019-02-21
  • 1970-01-01
  • 2012-02-01
  • 2023-03-04
相关资源
最近更新 更多