是否有 C 等效于 Rust 的 NonNull<T>::dangling() 指针实例化?
Is there a C equivalent to Rust's NonNull<T>::dangling() pointer instantiation?
如果存在,则应满足以下性质:
- 具有类型
void *
- 不需要实例化一个"dummy object"作为地址
- 保证不比较等于
NULL
- 可以在不调用未定义行为的情况下构造
- 无需非标准扩展即可使用符合标准的编译器
起初我以为我可以做一些类似(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.
您将空指针与空指针常量混淆了 NULL
。 NULL
可以扩展为 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 取消引用。由于最大对齐要求通常只有几个字节,因此这永远不会指向有效内存。
如果存在,则应满足以下性质:
- 具有类型
void *
- 不需要实例化一个"dummy object"作为地址
- 保证不比较等于
NULL
- 可以在不调用未定义行为的情况下构造
- 无需非标准扩展即可使用符合标准的编译器
起初我以为我可以做一些类似(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.
您将空指针与空指针常量混淆了 NULL
。 NULL
可以扩展为 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 取消引用。由于最大对齐要求通常只有几个字节,因此这永远不会指向有效内存。