1

When compiled as a 32-bit process, the following code prints ffffffff82223333 instead of 82223333, so it seems like a pointer is always sign-extended when converted to uint64_t. Why is that?

#include <stdint.h>
#include <stdio.h>

int main()
{
    void *p = (void*) 0x82223333;
    uint64_t x = (uint64_t) p;
    printf("%llx\n", x);
}

I thought an address can never be negative, so it should be treated as unsigned.

My question is also related to this question (because Windows handles are just typedefs for pointers).

Similar question, but still no explanation why the compiler developers choose to do this.

Community
  • 1
  • 1
zett42
  • 25,437
  • 3
  • 35
  • 72
  • I can't reproduce here; can you share more details of your environment? – Carl Norum Mar 26 '17 at 23:03
  • @Carl Windows with VS2017. – zett42 Mar 26 '17 at 23:03
  • Your reasoning seems a little reversed - whether addresses can be negative or not is a question that you can only answer *after* deciding whether to treat them as signed or unsigned. Anyway [this](https://msdn.microsoft.com/en-us/library/aa384242(v=vs.85).aspx) mentions that it's normal, but does not explain why this choice was made. – harold Mar 26 '17 at 23:12
  • 3
    The high-order 32-bits have no meaning in a 32-bit process, so the compiler ought to be able to do whatever it likes. The only guarantees you have with pointers is that if you convert one to a sufficiently-sized integer (e.g. `intptr_t`) and then back to a pointer, you get the same address again. – paddy Mar 26 '17 at 23:12
  • 1
    @paddy Yes, it's clear that this is implementation-dependent behaviour, but *why* did they choose to do this? – zett42 Mar 26 '17 at 23:44
  • @zett42 It might not have been a deliberate choice. It might happen to be a consequence of how the typecasting code is written to cover more valid cases. – Schwern Mar 26 '17 at 23:56
  • 1
    what is `sizeof(void *)` on your system ? Try `printf("%p\n", p);` – M.M Mar 27 '17 at 00:49
  • @M.M it's 4 bytes – zett42 Mar 27 '17 at 00:51
  • 1
    Maybe it handles pointer-integer conversion by converting the pointer to `long` and then converting that result to whatever type is requested – M.M Mar 27 '17 at 00:52
  • Why does it matter? If you absolutely require the number to have its top bits cleared, then use a bitmask. But beware that masking and then converting back to a pointer might be undefined behaviour. – paddy Mar 27 '17 at 01:16
  • @paddy I'm just curious because OP of [this question](http://stackoverflow.com/q/43012454/7571258) observed that behaviour and I couldn't really explain it. His question is solved by casting to `uint32_t` instead because handles are always 32-bit. In general I think I can live with "it's implementation-defined behaviour". Thanks. – zett42 Mar 27 '17 at 01:19
  • 1
    My guess: 32 bit windows uses top half as kernel memory, and 64 bit windows uses top half of the "canonical form addresses " as kernel memory. (https://en.wikipedia.org/wiki/X86-64#VIRTUAL-ADDRESS-SPACE). Possibly Windows is trying to use this convention to map kernel/user 32 bit addresses to kernel/user 64 bit addresses respectively. Use case? To support legacy 32 bit kernel mode driver ABI? Maybe? – user3528438 Mar 27 '17 at 15:15
  • 1
    More: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384242(v=vs.85).aspx http://stackoverflow.com/questions/3447812/pointer-32-what-is-it-and-why – user3528438 Mar 27 '17 at 15:21

0 Answers0