Любой реальный пример reinterpret_cast, изменяющий значение указателя?
В соответствии со стандартом С++ a reinterpret_cast
указателя T*
другому указателю типа Q*
может изменять или не изменять значение указателя в зависимости от реализация.
Мне очень интересно - есть ли какой-нибудь реальный пример реализации С++, где кастинг указателя на какой-то другой тип указателя с помощью reinterpret_cast
изменяет указатель? Что и почему там изменилось?
Ответы
Ответ 1
Обратите внимание, что когда стандарт утверждает, что он может или не может что-то сделать, это не означает, что существует какая-либо текущая реализация, которая имеет такое поведение, только то, что они могли.
Ближайшим, о котором я могу думать, является архитектура, в которой для аппаратного обеспечения требуется согласование типов, а также реализация, которая решила исправить выравнивание, если это необходимо. Что-то вроде:
aligned8 var;
aligned1 *p = reinterpret_cast<aligned1*>(&var);
aligned1 *q = p + 1; // assuming aligned 1 size is not multiple of 8
aligned8 *a = reinterpret_cast<aligned8*>(q); // [1]
Может существовать требование, чтобы для a
был действительный указатель, он должен адресовать позицию памяти, кратную 8, а аргумент q
с меньшими требованиями к выравниванию может указывать на любой адрес памяти.
Ответ 2
class A1 { int a1; };
class A2 { int a2; };
class B: public A1, public A2 { };
#define DBG(val) cout << #val << ": " << val << endl
// test code
B b;
DBG(&b); // prints 0x42
void *p_blank = &b;
DBG(p_blank); // prints 0x42
A2 *p_a2 = &b;
DBG(p_a2); // prints 0x46
void *p_reinterpreted = reinterpret_cast<A2*>(&b);
DBG(p_reinterpreted); // prints 0x42
A2 *p_reinterpreted2 = reinterpret_cast<A2*>(&b);
DBG(p_reinterpreted2); // prints 0x42
A2 *p_a2 = &b
означает дать мне указатель на объект A2 внутри объекта B.
reinterpret_cast<A2*>(&b)
означает дать мне указатель на b и рассматривать его как указатель A2.
Результат этого reinterpret_cast имеет тип "указатель на A2", поэтому он не выдает предупреждения при назначении переменной void * (или переменной A2 *).
Ответ 3
Наиболее вероятный источник проблем находится на векторной машине, где скалярные операции определяются в терминах векторов, а скалярный указатель состоит из указателя на вектор с индексом в вектор. Исторически первоначальная архитектура Cray была такой, и это вызывало головные боли. В настоящее время вы можете увидеть что-то подобное на GPU, но я не могу указать что-то конкретное с головы.
Наиболее вероятным эффектом является усечение, поскольку тип указателя адресата не имеет битов, чтобы указать индексную часть. С++ 11 дает кивок в этом направлении, позволяя любым типам указателей быть reinterpret_cast
ed, если они имеют одинаковые требования к выравниванию. Биты, "обнуленные" строгим выравниванием, не могут существовать.
Указатель объекта может быть явно преобразован в указатель объекта другой тип. Когда prvalue v типа "указатель на T1" является преобразованный в тип "указатель на cv T2", результатом является static_cast<cv
T2*>(static_cast<cv void*>(v))
, если оба T1 и T2 являются стандартными макетами типы (3.9) , а требования к выравниванию T2 не являются более строгими, чем те из T1, или если любой тип недействителен. Преобразование prvalue типа "указатель на T1" на тип "указатель на T2" (где T1 и T2 равны типы объектов и где требования к выравниванию T2 отсутствуют более строгие, чем у T1) и обратно к своему первоначальному типу исходное значение указателя. Результат любого другого такого указателя преобразование не указано.
Ответ 4
Я не думаю, что вопрос имеет смысл для С++ и C-указателей. Из этого ответа Я цитирую только один из примеров:
Серия Eclipse MV из Data General имеет три архитектурно поддерживаемых формата указателя (слова, байты и указатели на бит), два из которых используются компиляторами C: указатели байтов для char*
и void*
, а указатели слов для все остальное
Это говорит о том, что reinterpret_cast<Word_Aligned_Type*>(char*)
может потерять смысл, о котором указывает символ/байт в слове, делая операцию необратимой.
Ответ 5
Reinterpret_cast никогда не вернет другой адрес - требуется скопировать точный адрес.
В случаях множественного наследования, как сказал Дэвид Родригес, если принять адрес одной из баз, можно вернуть адрес, который имеет смещение по адресу первой базы. Reinterpret_cast вернет этот адрес смещения, но если вы будете рассматривать его как адрес upcast, произойдет ад.
Для повышения уровня, static_cast может возвращать другой адрес, чем тот, который указан. Если адрес, который у вас есть, является одним из оснований, и этот адрес находится на смещении к первому базовому адресу, static_cast вернет действительный адрес для объекта с повышением уровня, который равен адресу первой базы и, следовательно, не равен к указателю.
Чтобы сделать это коротко: reinterpret_cast дает вам тот же адрес, всегда. Static_cast и dynamic_cast могут возвращать другой адрес, например. в некоторых случаях с множественным наследованием.
Разница между static_cast и dynamic_cast заключается в том, что static_cast не проверяет, является ли указатель, который вы ему указываете, правильным объектом для трансляции, поэтому будьте уверены, прежде чем вызывать его.