Правильный способ ввода типов указателей
Учитывая следующий код (и тот факт, что VirtualAlloc()
возвращает a void*
):
BYTE* pbNext = reinterpret_cast<BYTE*>(
VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));
почему reinterpret_cast
выбран вместо static_cast
?
Раньше я думал, что reinterpret_cast
в порядке, например. (например, DWORD_PTR
), но отличать от void*
до a BYTE*
не означает static_cast
ОК?
Существуют ли какие-либо (тонкие?) различия в этом конкретном случае, или они являются как действительными действительными указателями?
Предпочитает ли стандарт С++ этот случай, предлагая способ вместо другого?
Ответы
Ответ 1
Либо заливка допустима для указателей на основные типы, поэтому вы правы, что static_cast
в порядке.
При преобразовании двух типов указателей возможно, что определенный адрес памяти, содержащийся в указателе, должен измениться.
То, что две отливки отличаются. static_cast
сделает соответствующую настройку. reinterpret_cast
избежит изменения хранимого значения указателя.
По этой причине это хорошее правило для static_cast
между типами указателей, если вам не нравится знать, что reinterpret_cast
.
Ответ 2
Вы должны static_cast
. Используйте static_cast
в тех случаях, когда вы отменяете неявное преобразование.
В этом конкретном случае, однако, нет разницы, потому что вы переходите из void*
. Но в общем случае reinterpret_cast
между двумя указателями объектов определяется (§5.2.10/7):
Указатель объекта может быть явно преобразован в указатель объекта для определенного типа. Когда prvalue v
типа "указатель на T1
" преобразуется в тип "указатель на cv T2
", результат static_cast<cv T2*>(static_cast<cv void*>(v))
, если оба T1
и T2
являются стандартными типами макета, а требования к выравниванию T2
не более строгие, чем требования для T1
, или если один из типов - void
. Преобразование prvalue типа "указатель на T1
" в тип "указатель на T2
" (где T1
и T2
- это типы объектов и где требования к выравниванию T2
не более строгие, чем требования T1
) и обратно к исходному типу дает исходное значение указателя. Результат любого другого такого преобразования указателя не определен.
Акцент мой. Поскольку T1
для вас уже void*
, приведение в void*
в reinterpret_cast
ничего не делает. Это не так в общем, что это Дрю Дорманн говорит:
#include <iostream>
template <typename T>
void print_pointer(const volatile T* ptr)
{
// this is needed by oversight in the standard
std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}
struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};
int main()
{
derived d;
base_b* b = &d; // implicit cast
// undo implicit cast with static_cast
derived* x = static_cast<derived*>(b);
// reinterpret the value with reinterpret_cast
derived* y = reinterpret_cast<derived*>(b);
print_pointer(&d);
print_pointer(x);
print_pointer(y);
}
Вывод:
00CBFD5B
00CBFD5B
00CBFD5C
(Обратите внимание, что поскольку y
на самом деле не указывает на derived
, используя его поведение undefined.)
Здесь reinterpret_cast
появляется другое значение, потому что оно проходит через void*
. Вот почему вы должны использовать static_cast
, когда можете, и reinterpret_cast
, когда вам нужно.
Ответ 3
Использование static_cast
для указания указателя на и из void*
гарантирует сохранение адреса.
reinterpret_cast
, с другой стороны, гарантирует, что если вы отбросите указатель от одного типа к другому и вернитесь к исходному типу, адрес будет сохранен.
Хотя в большинстве реализаций вы увидите одни и те же результаты при использовании любого из них, static_cast
должен быть предпочтительным.
И с C++11
я помню, что использование reinterpret_cast
для void*
имеет четко определенное поведение. До этого такое поведение было запрещено.
It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.
Предлагаемая резолюция (август 2010 г.):
Измените 5.2.10 [expr.reinterpret.cast] пункт 7 следующим образом:
Указатель объекта может быть явно преобразован в указатель объекта другой тип. Когда prvalue v типа "указатель на T1" является преобразованный в тип "указатель на cv T2", результат static_cast (static_cast (v)), если оба T1 и T2 являются стандартными макетами типы (3.9 [basic.types]) и требования к выравниванию T2 не являются более строгие, чем у T1, или если какой-либо тип недействителен.
Преобразование prvalue типа "указатель на T1" в тип "указатель на T2" (где T1 и T2 являются типами объектов и где выравнивание требования T2 не более строгие, чем требования T1) и обратно оригинальный тип дает исходное значение указателя. Результат любого другое преобразование указателя не указано.
Подробнее здесь.
Благодаря Jesse Good для ссылки.