Pointer arithmetic on Windows

Given a pointer (ptr), on Windows rustc 1.53.0-nightly, the following code is displaying the exact same value for both pointers:

unsafe {
  println!("PTR: {:?} END: {:?}", ptr, ptr.add(0x8888));
}

After reading the docs for the add() function (pointer - Rust) I thought that the above should print two different values, and not the same value for both pointers. My initial thought is that I must misunderstand how rust is doing pointer arithmetic.

Can anyone say why the same value is being displayed for both PTR and END?

I'd assume you violated the first bullet point of the safety comment, which means this is UB. All bets are of in that case.

I have narrowed the problem down to an issue with c_void pointers. The original code uses the WinPcap external library. (See https://github.com/libpnet/libpnet/blob/master/pnet_datalink/src/winpcap.rs) I have distilled the issue down to a simple snippet that demonstrates the behavior where add() does not seem to work. I have tried using simple types like pointers into a constant string, which work fine, so I am assuming there is some sort of type wizardry going on with external C pointers that I do not understand.

        // from elsewhere:  pub type PVOID = *mut ctypes::c_void;
        unsafe {
            let mut b = Vec::new();
            b.resize(1000, 0u8);
            let bp: *mut ctypes::c_void = b.as_mut_ptr() as winpcap::PVOID;
            println!("PRE: {:?} PTR: {:?} END: {:?}", bp.sub(0x2), bp, bp.add(0x2));
        }

(All three values -- PRE, PTR, and END -- display the same value.)

Isn't this undefined behaviour? That adress is not in bounds of the vector's buffer.

If c_void is a zero sized type then this is expected behaviour, it is correctly adding 0 * 0x2 = 0 to the pointer.

I would expect bp.sub(0x2) to fail or be undefined, but bp and bp.add(0x2) both return the same value.

I believe the size associated with the c_void pointer derives from the type of 0u8 -- it is a pointer to a buffer of unsigned bytes.

By default, winapi::ctypes::c_void is defined as pub enum c_void {}, which has size 0.

(However, if the winapi/std Cargo feature is enabled, then this becomes an alias for std::ffi::c_void, which has size 1.)

Success -- thank you! Enabling the std feature of the winapi dependency worked.

For reference, this change in Cargo.toml makes the pointers work as expected:

[target.'cfg(windows)'.dependencies]
# winapi = "0.3.8" # This will not work because the size of c_void defaults to 0.
winapi = { version = "0.3.8", features = [ "std" ] }

I only now see what you are saying -- I had thought c_void would have inferred the size from the 0u8 field from the resize.