Подтвердить что ты не робот

Правильный способ ввода типов указателей

Учитывая следующий код (и тот факт, что 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 ОК?

Существуют ли какие-либо (тонкие?) различия в этом конкретном случае, или они являются как действительными действительными указателями?

Предпочитает ли стандарт С++ этот случай, предлагая способ вместо другого?

4b9b3361

Ответ 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 для ссылки.