【问题标题】:Is it legal/safe to cast away `const` for a heap-allocated object?为堆分配的对象抛弃“const”是否合法/安全?
【发布时间】:2019-05-03 06:50:57
【问题描述】:

我的用例如下。

我开发了一个库,其中一些加载的插件可以创建对象(由库使用malloc() 分配),而其他一些插件可以读取这些对象的属性但不能修改它们。

对我来说,这是一个非const API 用于创建/编写端和const API 用于读取端的情况,例如:

// writer API
struct obj *obj_create(void);
void obj_set_some_property(struct obj *obj, int property);

// reader API
int obj_get_some_property(const struct obj *obj);

库将struct obj *(由作者方创建)转换为const struct obj *(读者方可用)。

我的问题是那些对象也有引用计数,读者端可以调用你典型的引用计数递增/递减函数。这些函数需要修改对象。

我的问题是:在这种特定情况下,引用计数递增/递减函数在内部接受 const struct obj * 并丢弃 const 是否安全?请注意,如果计数达到零,引用计数递减函数也可能销毁(释放)对象。

我知道 §6.7.3¶5 说:

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

我只是想不通用 const 限定类型定义 是什么意思。如果我的对象是堆分配的,这是否适用?例如,我完全理解为什么使用文字字符串指针 (.rodata) 会是 UB。但是如果对象一开始就被创建为非const 呢?

strchr() 是一个众所周知的将const 强制转换的示例:它接受const char * 并返回char *,它指向const char * 参数内。考虑到 §6.7.3¶5,这在法律上如何?

【问题讨论】:

  • 这完全取决于原始指针是如何分配的。 const 限定符主要允许编译器在尝试修改时通过警告来帮助您,并允许编译器根据您不会修改对象的承诺进一步优化。正如您所发现的,您可以通过抛弃const 限定符来打破这个承诺,但是.. 那么您就靠自己了,您必须保证内存是可变的,在free 的情况下,之前分配有@987654340 @。抛弃的需要通常表明代码设计存在缺陷。
  • 强制转换不会修改对象。抛弃 const 绝不是 UB。如果您继续使用强制转换的结果来修改 const 对象,则可能是 UB。
  • 语言律师的东西:重要的是对象的有效类型。 malloc 指向的数据在您对数据进行左值访问之前没有类型 - 在这种情况下,“有效类型”将成为您用于访问的类型。而且我认为不可能使用 const 限定类型(?)进行左值访问。

标签: c casting constants undefined-behavior const-correctness


【解决方案1】:

malloc 分配的对象没有任何类型,更不用说 const 限定的了。无论您是否在某个阶段使用指向 const 的指针指向对象,都可以修改它们。

“使用 const 限定类型定义”要求对象具有定义(malloc 是函数调用,而不是定义)。

【讨论】:

    【解决方案2】:

    在此上下文中,defined 是指定义变量的程序语句,例如const struct obj x = {};。这与仅声明它的声明相反,例如const struct obj* x;

    在 C 中,malloc 返回的内存是可以安全写入的未初始化存储。事实上,在将结构传递给客户端之前,您的库必须至少完成一次!

    理论上,如果客户端以某种方式声明 const obj x = OBJ_INITIALIZER; 然后将其传递给您的库,您可能会遇到问题。您的编译器可能已将该变量定义粘贴到内存的只读页面中,或者可能在假设它永远无法修改的情况下过于激进地优化。因此,您需要指定任何在内部丢弃 const 的库函数适用于其自己工厂的对象。

    确保不受信任的客户端代码不会违反此假设的一种方法是传入句柄而不是对象指针,但您的库可能不需要打扰。

    如果客户端代码使用您修改的字段,并且如果编译器假定采用const struct obj* 参数的函数无法通过该参数修改它,您也可能会遇到问题。你可以通过返回一个别名对象的引用来解决这个问题,编译器不会假设它是同一个未修改的对象(或者,我猜,通过滥用volatile)。

    在 C++ 中,您可以选择声明需要修改的字段mutable

    【讨论】:

    • 我的库确实只使用了不透明的句柄(参见obj_create() 示例),所以我认为我没有初始化程序问题。 如果编译器假定一个带有const struct obj * 参数的函数不能通过该参数修改它:这不会使编译器崩溃,因为它只是一个const(参数)声明在此刻?这是使用初始化程序而不是句柄的缺点吗?
    • @eepp 如果您只传递不透明的句柄,则不会出现任何问题。 (但您可能可以避免将指针传递给您需要以这种方式强制转换的结构。)
    猜你喜欢
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多