是否 legal/safe 放弃堆分配对象的 `const`?
Is it legal/safe to cast away `const` for a heap-allocated object?
我的用例如下。
我开发了一个库,其中一些加载的插件可以创建对象(由库使用 malloc()
分配),而其他一些插件可以读取这些对象的属性但不能修改它们。
对我来说,这是 creating/writer 侧有一个非 const
API 而 [=] 侧有一个 const
API 的情况53=]边,例如:
// 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 *
(可用于 reader 方)。
我的问题是那些对象也有引用计数,reader 端可以调用典型的引用计数 incrementation/decrementation 函数。那些函数需要修改对象。
我的问题是:在这种特定情况下,引用计数 incrementation/decrementation 函数在内部接受 const struct obj *
并丢弃 const
是否安全?请注意,如果计数达到零,引用计数递减函数也可以销毁(释放)对象。
我知道§6.7.3¶5 说:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
我只是想不通 defined with a const-qualified type 是什么意思。如果我的对象是堆分配的,这是否适用?例如,我完全理解为什么使用文字字符串指针 (.rodata
) 会是 UB。但是如果对象一开始就被创建为非const
怎么办?
strchr()
是一个众所周知的转换 const
的例子:它接受 const char *
和 returns char *
,它们指向 [=23] =] 参数。考虑到 §6.7.3¶5 这是否合法?
malloc
分配的对象没有任何类型,更不用说 const 限定的对象了。无论您是否在某个阶段使用指向常量的指针指向对象,都可以修改它们。
"defined with a const-qualified type" 要求对象有定义(malloc
是函数调用,不是定义)。
在此上下文中,defined指定义变量的程序语句,例如const struct obj x = {};
。这与仅仅声明它的语句相反,例如 const struct obj* x;
.
在 C 中,malloc
返回的内存是可以安全写入的未初始化存储。事实上,在将结构传递给客户端之前,您的库必须至少这样做过一次!
理论上,如果客户端以某种方式声明了一个 const obj x = OBJ_INITIALIZER;
然后将其传递给您的图书馆,您可能会遇到问题。您的编译器可能将该变量定义粘贴到内存的只读页面中,或者可能基于永远无法修改的假设而过度优化。因此,您需要指定任何在内部丢弃 const
的库函数 仅 对其自身工厂中的对象起作用。
确保不受信任的客户端代码不会违反此假设的一种方法是传入句柄而不是对象指针,但您的库可能不需要麻烦。
如果客户端代码使用您修改的字段,并且编译器假定采用 const struct obj*
参数的函数不能通过该参数修改它,那么您也可能 运行 遇到问题。您可以通过返回一个为对象起别名的引用来解决这个问题,编译器不会假定它是同一个未修改的对象(或者,我猜,通过滥用 volatile
)。
在 C++ 中,您可以选择声明需要修改的字段 mutable
。
我的用例如下。
我开发了一个库,其中一些加载的插件可以创建对象(由库使用 malloc()
分配),而其他一些插件可以读取这些对象的属性但不能修改它们。
对我来说,这是 creating/writer 侧有一个非 const
API 而 [=] 侧有一个 const
API 的情况53=]边,例如:
// 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 *
(可用于 reader 方)。
我的问题是那些对象也有引用计数,reader 端可以调用典型的引用计数 incrementation/decrementation 函数。那些函数需要修改对象。
我的问题是:在这种特定情况下,引用计数 incrementation/decrementation 函数在内部接受 const struct obj *
并丢弃 const
是否安全?请注意,如果计数达到零,引用计数递减函数也可以销毁(释放)对象。
我知道§6.7.3¶5 说:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined.
我只是想不通 defined with a const-qualified type 是什么意思。如果我的对象是堆分配的,这是否适用?例如,我完全理解为什么使用文字字符串指针 (.rodata
) 会是 UB。但是如果对象一开始就被创建为非const
怎么办?
strchr()
是一个众所周知的转换 const
的例子:它接受 const char *
和 returns char *
,它们指向 [=23] =] 参数。考虑到 §6.7.3¶5 这是否合法?
malloc
分配的对象没有任何类型,更不用说 const 限定的对象了。无论您是否在某个阶段使用指向常量的指针指向对象,都可以修改它们。
"defined with a const-qualified type" 要求对象有定义(malloc
是函数调用,不是定义)。
在此上下文中,defined指定义变量的程序语句,例如const struct obj x = {};
。这与仅仅声明它的语句相反,例如 const struct obj* x;
.
在 C 中,malloc
返回的内存是可以安全写入的未初始化存储。事实上,在将结构传递给客户端之前,您的库必须至少这样做过一次!
理论上,如果客户端以某种方式声明了一个 const obj x = OBJ_INITIALIZER;
然后将其传递给您的图书馆,您可能会遇到问题。您的编译器可能将该变量定义粘贴到内存的只读页面中,或者可能基于永远无法修改的假设而过度优化。因此,您需要指定任何在内部丢弃 const
的库函数 仅 对其自身工厂中的对象起作用。
确保不受信任的客户端代码不会违反此假设的一种方法是传入句柄而不是对象指针,但您的库可能不需要麻烦。
如果客户端代码使用您修改的字段,并且编译器假定采用 const struct obj*
参数的函数不能通过该参数修改它,那么您也可能 运行 遇到问题。您可以通过返回一个为对象起别名的引用来解决这个问题,编译器不会假定它是同一个未修改的对象(或者,我猜,通过滥用 volatile
)。
在 C++ 中,您可以选择声明需要修改的字段 mutable
。