C имеет тип uintptr_t
, который позволяет хранить значение указателя:
void *p = …;
uintptr_t u = (uintptr_t)p;
void *q = (void *)q;
// here q is equivalent to p
На многих платформах u
хранит адрес объекта, который p
указывают, как и p
, и u
и p
имеют одинаковое представление в памяти.Это не обязательство: реализациям C разрешается использовать другое представление.Но использование того же представления является наиболее очевидным способом выполнения требования стандарта C.
Обратите внимание, что преобразование из uintptr_t
в тип указателя дает пригодный для использования указатель, только если было получено значение uintptr_t
от указателя, который все еще действителен.Это может быть тонким.Даже на процессорных архитектурах, где представление одинаково (а это почти все), компиляторы могут предполагать, что вы не делаете слишком сумасшедших вещей, и оптимизировать их соответствующим образом.См. ответы на опрос Cerberus для некоторых примеров.
В вашем случае вы не пытались сохранить значение указателя в uintptr_t
, вы пытались сохранить его в int
.Это не удалось, поскольку на вашей платформе int
слишком мало для хранения значения указателя.Очевидно, что на вашей платформе указатели являются 64-битными значениями, а int
- 32-битными значениями.Это очень распространенная схема.
a
- адрес массива.(Это указатель на первый элемент массива, а на вашей платформе указатель просто представлен адресом.).В данном конкретном прогоне это 0x7ffee8d30600.Ничего примечательного там нет. (int) a
преобразует адрес a
в значение int
.Это определяется реализацией.Ваша реализация делает обычное дело: поскольку int
слишком мало, оно усекает значение.Усечение указанного выше адреса до 32-разрядного значения без знака дает 0xe8d30600
.Чтобы получить значение со знаком, старший бит становится знаковым битом, поэтому a
оказывается отрицательным: a
равно -388823552
. p
равно (int *) ((int) a)
.Это преобразование снова определяется реализацией, и ваша реализация снова делает то же самое, что и большинство других.Он принимает 32-битное значение int
со знаком -388823552
, расширяет его до желаемой ширины и смещает в диапазон без знака, получая 2 64 - 388823552 = 0xffffffffe8d30600. (int)p
применяет тот же процесс усечения, что и (int)a
выше, и снова выдает -388823552
.Но вы используете спецификатор %x
для его распечатки, поэтому это значение преобразуется в unsigned
(32-битный тип на вашей платформе), что дает значение 2 32 - 388823552 =0xe8d30600.