Search code examples
cpointersvoid-pointersdangling-pointer

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


If it exists, it should satisfy the following properties:

  • Has the type void *
  • Does not require the instantiation of a "dummy object" to act as the address
  • It is guaranteed to not compare equal to NULL
  • Can be constructed without invoking undefined behavior
  • Works with standards-conforming compiler without needing non-standard extensions

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. The second relies on the fact that NULL does not have physical address 1. (i.e. it is entirely possible that (void *)0 == (void *)1)


Solution

  • NonNull::dangling() exists in Rust to be able to temporarily initialize a NonNull value before giving it the real value. You cannot use null as the temporary because it is a NonNull and it would render Undefined behavior.

    For example this perfectly safe (I guess) self-referentiable example requires 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
        }
    }
    

    About your question of the equivalent to NonNull::dangling() in C is that, in C there is no NonNull, so for these kinds of temporary initialization you can NULL or just leave it unitilizalized until you have the proper value.

    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;
    }
    

    That said, I'm sure that there are other uses of NonNull::dangling other than temporary initialization of self-referentiable structs. For those you may actually need an equivalent C code. The equivalent C code would be (in macro form as it takes a type as argument):

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

    That is, a pointer as near to zero as possible while complying with the alignment of the given type. The idea is that in most architectures the NULL pointer is actually at address 0, and the first few kilobytes are never mapped, so that the runtime can catch NULL dereferences. And since the maximum alignment requirements are usually just a few bytes, this will never point to valid memory.