是否有 C 等效于 Rust 的 NonNull<T>::dangling() 指针实例化?

Is there a C equivalent to Rust's NonNull<T>::dangling() pointer instantiation?

如果存在,则应满足以下性质:

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

任何空指针都能满足您的所有要求。

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

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

这完全符合标准。结果是实现定义的,因为分配的有效地址和对齐之类的东西是系统特定的。

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


At first I thought I could do something like (NULL + 1) or (void *)1, but these appear to be problematic. The former uses pointer arithmetic on NULL which I believe is undefined behavior.

您将空指针与空指针常量混淆了 NULLNULL 可以扩展为 0(void*)0

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

NonNull::dangling() 存在于 Rust 中,以便能够在给它真正的值之前临时初始化一个 NonNull 值。您不能将 null 用作临时值,因为它是 NonNull 并且会呈现未定义的行为。

例如,这个完全安全(我猜)的自引用 example 需要 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 取消引用。由于最大对齐要求通常只有几个字节,因此这永远不会指向有效内存。