Любой реальный пример 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 не проверяет, является ли указатель, который вы ему указываете, правильным объектом для трансляции, поэтому будьте уверены, прежде чем вызывать его.