是否有一个 C 等效于 Rust 的非空<T>::d angling() 指针实例化?



如果存在,它应该满足以下属性:

  • 具有类型void *
  • 不需要实例化"虚拟对象"作为地址
  • 保证不等于NULL
  • 可以在不调用未定义行为的情况下构造
  • 与符合标准的编译器配合使用,无需非标准扩展

起初我以为我可以做一些像(NULL + 1)(void *)1的事情,但这些似乎有问题。前者在NULL上使用指针算法,我认为这是未定义的行为。第二个依赖于NULL没有物理地址 1 的事实。(即完全有可能(void *)0 == (void *)1)

任何 void 指针都可以满足您的所有要求。

只要您确定哪些地址是有效的,并且在特定系统上采用,您就可以手动创建这样的指针:

void* dangling = (void*)0x12345678; // an address which you know for sure isn't taken

这完全符合标准。结果是实现定义的,因为分配的有效地址和对齐方式等内容是特定于系统的。

至于这对你有什么好处,我不知道。当指针未设置为指向分配的地址时,应使用 Null 指针。


起初我以为我可以做类似 (NULL + 1) 或 (void *)1 之类的事情,但这些似乎有问题。前者在 NULL 上使用指针算法,我认为这是未定义的行为。

您正在将空指针与空指针常量NULL混淆。NULL可以扩展到0(void*)0.

  • 如果你做算术0 + 1你只需得到一个整数常量表达式1。如果需要,可以将其转换为指针,与上述相同的impl.defined行为,实际上等同于(void*)1
  • 如果你做算术(void*)0 + 1那么代码将无法编译,因为你不能在空指针上做算术。如果您在指针上执行指针算术而不指向分配的数组,则它是 UB。
NonNull::dangling()

存在于Rust 中,能够在给它实际值之前临时初始化一个NonNull值。不能将null用作临时,因为它是NonNull,并且会呈现未定义的行为。

例如,这个完全安全(我猜)的自引用示例需要NonNull::dangling()

struct SelfRef {
myself: NonNull<SelfRef>,
data: String,
}
impl SelfRef {
fn new(data: String) -> Pin<Box<SelfRef>> {
let mut x = Box::pin(SelfRef {
myself: NonNull::dangling(),
data,
});
x.myself = unsafe { NonNull::new_unchecked(x.as_mut().get_unchecked_mut()) };
x
}
}

关于你在 C 中等价于NonNull::dangling()的问题是,在 C 中没有NonNull,所以对于这些类型的临时初始化,你可以NULL或只是让它单位化,直到你有适当的值。

struct SelfRef {
SelfRef *myself;
//...
};
struct SelfRef *new_selfref() {
struct SelfRef *x = malloc(sizeof(struct SelfRef));
//Here x->myself is uninitialized, that is as good as dangling()
x->myself = x;
return x;
}

也就是说,我确信除了临时初始化自引用结构之外,NonNull::dangling还有其他用途。对于那些,您可能实际上需要一个等效的 C 代码。等效的 C 代码将是(以宏形式,因为它将类型作为参数):

#define DANGLING(T) ((T*)alignof(T))

也就是说,指针尽可能接近零,同时符合给定类型的对齐方式。这个想法是,在大多数体系结构中,NULL 指针实际上位于地址 0 处,并且前几千字节永远不会映射,以便运行时可以捕获 NULL 取消引用。由于最大对齐要求通常只有几个字节,因此这永远不会指向有效内存。

最新更新