为堆分配的对象丢弃`const`是否合法/安全?

时间:2018-12-01 08:09:32

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

我的用例如下。

我开发了一个库,其中一些已加载的插件可以创建对象(库使用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条如何合法?

2 个答案:

答案 0 :(得分:3)

Ctrl+Shift+I分配的对象没有任何类型,更不用说用const限定的类型了。无论在某个阶段是否用指向常量的指针指向对象,都可以对其进行修改。

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

答案 1 :(得分:2)

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

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

理论上,如果客户端以某种方式声明const obj x = OBJ_INITIALIZER;并将其传递给您的库,则可能会遇到问题。您的编译器可能已将该变量定义粘贴到内存的只读页面中,或者在假设它永远无法修改的前提下过分积极地进行了优化。因此,您需要指定所有在内部工厂中抛弃const的库函数仅对它们自己的对象起作用。

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

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

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