Что это такое uintptr_t
и для чего его можно использовать?
Что это такое uintptr_t
и для чего его можно использовать?
Ответы:
uintptr_t
является целочисленным типом без знака, который способен хранить указатель данных. Что обычно означает, что это тот же размер, что и указатель.
Опционально определяется в C ++ 11 и более поздних стандартах.
Распространенная причина для того, чтобы получить целочисленный тип, который может содержать тип указателя архитектуры, состоит в том, чтобы выполнять целочисленные операции над указателем или скрывать тип указателя, предоставляя его как целочисленный «дескриптор».
Изменить: Обратите внимание, что у Стива Джессопа есть несколько очень интересных дополнительных деталей (которые я не буду красть) в другом ответе здесь для вас педантичных типов :)
size_t
должен быть достаточным для хранения размера наибольшего объекта и может быть меньше указателя. Этого можно ожидать на сегментированных архитектурах, таких как 8086 (16 бит size_t
, но 32 бит void*
)
ptrdiff_t
. uintptr_t
не предназначен для этого.
unsigned int
обычно недостаточно велик. Но это может быть достаточно большим. Этот тип существует специально, чтобы удалить все «предположения» .
Во-первых, в то время, когда задавался вопрос, uintptr_t
не было в C ++. Это в C99, в <stdint.h>
качестве дополнительного типа. Многие компиляторы C ++ 03 предоставляют этот файл. Это также в C ++ 11, <cstdint>
где, опять же, это необязательно, и который ссылается на C99 для определения.
В C99 это определяется как «целочисленный тип без знака со свойством, что любой действительный указатель на void может быть преобразован в этот тип, затем преобразован обратно в указатель на void, и результат будет сравниваться равным исходному указателю».
Примите это, чтобы означать, что это говорит. Это ничего не говорит о размере.
uintptr_t
может быть такого же размера, как void*
. Это может быть больше. Вероятно, он может быть меньше, хотя такая реализация на C ++ подходит извращенно. Например, на некоторой гипотетической платформе, где void*
используется 32 бита, но используется только 24 бита виртуального адресного пространства, у вас может быть 24-бит, uintptr_t
который удовлетворяет требованию. Я не знаю, почему реализация сделала бы это, но стандарт разрешает это.
void*
. Это влияет на возможные будущие направления, тем не менее, особенно если вы можете захотеть изменить что-то, что на самом деле является просто целочисленным дескриптором, а не преобразованным указателем вообще.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. Вместо этого: callback.dataPtr = (void*)val
. С другой стороны, вы, конечно, получаете void*
и должны отбросить его обратно int
.
Это целочисленный тип без знака точно такого же размера, как указатель. Всякий раз, когда вам нужно сделать что-то необычное с указателем - как, например, инвертировать все биты (не спрашивайте, почему), вы uintptr_t
приводите его и манипулируете им как обычным целым числом, а затем возвращаете.
void*
значения указателя в uintptr_t
и обратно возвращает void*
значение, которое сравнивается с исходным указателем. uintptr_t
обычно имеет такой же размер, как и void*
, но это не гарантируется, и нет никакой гарантии, что биты преобразованного значения имеют какое-то конкретное значение. И нет никакой гарантии, что он может содержать преобразованное значение указателя на функцию без потери информации. Наконец, это не гарантировано.
Уже есть много хороших ответов на часть "что такое тип данных uintptr_t". Я попытаюсь ответить на вопрос "для чего это может быть использовано?" часть в этом посте.
В первую очередь для побитовых операций с указателями. Помните, что в C ++ нельзя выполнять побитовые операции с указателями. По причинам см. Почему вы не можете выполнять побитовые операции с указателем в C, и есть ли способ обойти это?
Таким образом, чтобы выполнять побитовые операции с указателями, нужно привести указатели к типу unitpr_t, а затем выполнить побитовые операции.
Вот пример функции, которую я только что написал для выполнения побитового исключения или двух указателей для хранения в связанном списке XOR, чтобы мы могли перемещаться в обоих направлениях, как список с двумя связями, но без штрафа за сохранение двух указателей в каждом узле ,
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
Риск получить еще один значок Necromancer, я хотел бы добавить одно очень хорошее использование для uintptr_t (или даже intptr_t), и это написание тестируемого встроенного кода. Я пишу в основном встроенный код, предназначенный для различных процессоров, а в настоящее время - процессоров. Они имеют различную внутреннюю ширину шины, а tenisilica - это фактически гарвардская архитектура с отдельным кодом и шинами данных, которые могут быть различной ширины. Я использую стиль разработки, основанный на тестировании, для большей части своего кода, что означает, что я делаю модульные тесты для всех блоков кода, которые я пишу. Модульное тестирование на реальном целевом оборудовании является проблемой, поэтому я обычно пишу все на ПК на базе Intel в Windows или Linux с использованием Ceedling и GCC. Это, как говорится, много встроенного кода включает в себя битовый твидинг и манипулирование адресами. Большинство моих машин Intel являются 64-битными. Так что, если вы собираетесь тестировать код манипулирования адресами, вам нужен обобщенный объект для математики. Таким образом, uintptr_t дает вам независимый от машины способ отладки кода перед тем, как вы попытаетесь развернуть его на целевом оборудовании. Другая проблема заключается в том, что для некоторых машин или даже моделей памяти в некоторых компиляторах указатели функций и указатели данных имеют разную ширину. На этих машинах компилятор может даже не разрешить приведение между двумя классами, но uintptr_t должен быть в состоянии удерживать любой из них.