← Back to context

Comment by sparkie

13 hours ago

What is the size of a pointer though?

On Intel 286 we had a 16-bit machine word and 24-bit addresses. A pointer wasn't just two machine words concatenated - the upper 8 bits were stored somewhere else - a segment register.

On modern machines we don't (usually) need to consider this because we have a single linear virtual address space, though the size is architecture dependant - usually above 40 bits and below 64. Most common size is 48-bits, but also up to 57-bits with 5 level paging enabled.

Either way we round up to 64-bits to store the pointer as one integer. C optionally provides types `intptr_t` and `uintptr_t`, which are integers large enough to hold the value of a pointer. Converting a pointer to `intptr_t` and back to the pointer type results in a pointer that compares equal to the original.

However, there is no guarantee that a pointer converted to `intptr_t` and back to a pointer can be dereferenced! It works most of the time because of our linear address space and non-use of segmentation, but segmentation can still be used - the FS and GS segment registers are still available on x86_64 and are commonly used for thread local storage. If you take a `thread_local T*`, convert it to `intptr_t`, and then convert it back to a `thread_local T*` on another thread and attempt to dereference it, then despite the pointers comparing equal, they dereference to different virtual addresses.

Integers tied to the size of a pointer would have been misguided. Pointers are not integers! (They just happen to use an integer in their representation).

Another one, `size_t` is supposed to represent the maximum size of any object. However, that's also not well-defined. The maximum object size on the Intel 256 would have been 16-bits, because that is all you can fit in a single segment.

On a modern machine, a `size_t` should really be 48-bits (4LP) or 57-bits (5LP), because we can't have an object larger than our maximum virtual address size - but `size_t` is typically 64-bits.

`thread_local` is a storage class, there is no `thread_local T*` in the same way there is no `static T*`

  • Yeah, but my point is that two pointers can compare equal but point to different addresses, thus it's not necessarily a safe operation to dereference a pointer cast from `intptr_r`.

    • I don’t know what you mean, if you take the address of a thread local variable you get a regular pointer which is just as safe to dereference as any other pointer, cast through intprt_t or not?

      2 replies →