Когда uintptr t предпочтительнее intptr t?
учитывая требование о том, что мне нужно хранить значение "общего" указателя в структуре и не иметь интереса к самой заостренной памяти, я считаю более семантически правильным хранить его как intptr_t
чем void*
. Вопрос Является ли uintptr_t
лучше подходит или нет, и когда один предпочтительнее другого в целом?
4 ответов
это в основном стилистический аргумент (оптимизирующий компилятор, вероятно, генерирует тот же или очень похожий код). Однако сравнение указателей может быть сложной проблемой.
помните, что в чисто стандартном c-указателе сравнение имеет смысл только для указателей на то же самое агрегированные данные. Вероятно, вам не разрешено сравнивать два результата из malloc
, например, для хранения отсортированного массива указателей.
Я бы сохранил их как void*
, либо как uintptr_t
. Подписанный intptr_t
имеет неудобство для сеггрегации отрицательных и положительных чисел, и там, где они исходят из значительных указателей приложений, это, вероятно, не приветствуется.
обратите внимание, что a void*
нельзя разыменовать: как uintptr_t
, вы есть чтобы бросить его, чтобы сделать что-то полезное с данными, указанными адресом; однако void*
указатели могут быть переданы в такие процедуры, как memset
PS. Я предполагаю обычный процессор (например, некоторые x86, PowerPC, ARM,...) с плоским виртуальным адресным пространством. Вы можете найти экзотические процессоры-возможно, некоторые DSPs - с очень существенными различиями (и, возможно, на которых intptr_t
не всегда имеет значение; помните, что в 1990-х годах Cray Y-MP суперкомпьютеры sizeof(long*) != sizeof(char*)
; в это время C99 не существовало, и я не уверен, что его <stdint.h>
может быть значимым на таких машины)
это звучит очень странно, так как для этого потребуются слепки. А void *
в C имеет огромное преимущество, что он преобразует в / из других типов указателей объектов без приведений,что является чистым.
, который сказал uintptr_t
может иметь смысл, если вы хотите сделать что-то с битами указателя, что вы не можете сделать так же разумно с целым числом со знаком (например, сдвинуть их вправо).
Если вы хотите манипулировать значением арифметически (например, зашифровать его), у вас гораздо больше гибкости с типом без знака (где арифметика обертывается), чем со знакомым типом (где арифметическое переполнение дает неопределенное поведение).
вы должны выбрать тип, подходящий для данной системы и программы. В большинстве случаев указатели являются положительными значениями адреса, и в этом случае uintptr_t
правильный тип. Но некоторые системы используют отрицательные адреса как способ выразить пространство ядра, как описано здесь: может ли указатель (адрес) не может быть отрицательной? это было бы причиной, по которой существуют два разных типа.
как (u)intptr_t
vs void*
для универсального типа указателя первый предпочитаемый в изрезанных, профессиональных программах. Существует много проблем / источников ошибок, связанных с типами указателей:
- все виды различных типов указателей чаще всего несовместимы друг с другом и не могут быть псевдонимами. Это проблема с указателями объектов, а также указателями функций.
- вы часто квалификаторы типа
const
, что делает преобразование указателей в / из этого типа сомнительным или плохо определенным. - преобразования в/из
void*
и другие типы указателей происходят неявно, что делает его легким для ошибок, связанных с использованием неправильного типа указателя, чтобы проскользнуть незамеченным. Это было исправлено в C++, но остается опасностью в C. возьмите, например, старый, но классический "я забыл включить stdlib.h при использовании malloc в C90 " ошибка. - выполнение арифметики на указателе имеет множество подводных камней, потому что вы можете безопасно выполнять арифметику только на указателе, который указывает на выделенный массив. Тем не менее, часто можно иметь адрес памяти по многим другим причинам, кроме указания на массив, как знает любой, кто работает со встроенными системами.
- вы не могу даже выполнять арифметические вычисления указателя на
void*
. Это зависит от нестандартного расширения компилятора.
это, как говорится, много устаревшего кода полагается на void
указатели, и совершенно нормально использовать их в ограниченном контексте. Некоторые примеры-канонический код, основанный на generic функции обратного вызова: bsearch
, qsort
, pthreads и подобные.
однако я бы не рекомендовал использовать void
указатели при разработке новых программ на языке с-их, на мой взгляд, лучше всего рассматривать как опасную особенность прошлого. В настоящее время существуют лучшие и более безопасные методы программирования generic C, такие как C11 _Generic
, трюки с использованием назначенных инициализаторов, передача параметров в качестве указателей массива (на VLA), проверка границ во время компиляции с помощью static_assert
etc. Некоторые примеры могут быть нашел в моем ответе здесь:как создать тип безопасные перечисления?.