A pointer may be converted to an integer, because C 2018 6.3.2.3 6 says:
Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined…
Furthermore, note 69 says:
The mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment.
Notes are not normative parts of the standard, but this tells us that for a “regular” C implementation, we should expect converting a pointer to an integer to yield the hardware memory address of the pointed-to thing, if it fits in the integer type. Note that some C implementations are designed for specific purposes, so they may be “irregular.” For example, a C implementation could be designed to be space efficient and use narrower pointers than the hardware supports.
A proper integer type to use for pointer-to-integer conversion is uintptr_t
, which is defined in <stdint.h>
. This is because 7.20.1.4 1 defines uintptr_t
to be capable of holding (all the information of) object pointers:
The following type designates an unsigned integer type with the property that any valid pointer to void
can be converted to this type, then converted back to pointer to void
, and the result will compare equal to the original pointer:
uintptr_t
In common modern hardware, memory addresses are simple integers in a “flat” address space. Bytes are numbered consecutively from 0 to whatever the maximum is. Each memory location corresponds to one number, and each number in this range corresponds to one memory location. (However, the fact that an address exists to designate a memory location does not mean the memory location is mapped or accessible in a particular process’ address space.)
Older machines had a variety of memory address schemes. Some schemes involved combinations of base address and offsets. In these machines, addresses had two or more parts, such as a 16-bit base b and an 16-bit offset o. When a pointer was converted to an integer, the result would be a 32-bit integer with b in the high bits and o in the low bits, equal to 65536•b + o. However, these integers did not consecutively number the addresses. When base b and offset o was used to access memory, the actual hardware address formed might be 64•b + o.
One effect of this is that b+1, o and b, o+64 are different addresses for the same memory location. Another effect is that subtracting the two integers that resulted from converting pointers would not necessarily give you the distance between them. The distance between b+1, o and b, o is 64 bytes, but subtracting 65536•b + o from 65536•(b+1) + o gives 65536.