C++ reinterpret_cast целое число
Я наткнулся на следующий код C++:
#define OFFSETOF_MEMBER(t, f) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(16)->f) - static_cast<uintptr_t>(16u)) // NOLINT
где t это тип, f это имя поля. Интересно, почему мы можем поставить целое число 16 в качестве параметра reinterpret_cast.
3 ответа
16 - это адрес, который мы назначаем указателю, который позволяет нам вычислить смещение указанного члена. Адрес указателя - просто число, поэтому мы можем злоупотреблять этим фактом, чтобы получить информацию о наших структурах / классах.
Скажем, у нас есть структура:
struct point {
//Assuming 32-bit integer sizes.
//For 64-bit integersizes, 0x0, 0x8, 0x10 for the integer offsets
int x; //Offset 0x0
int y; //Offset 0x4
int z; //Offset 0x8
}; static_assert(sizeof(point) == 12 /* or 0xC in hex */);
Мы используем макрос:
OFFSETOF_MEMBER(point, y);
Развернув макрос, мы получим:
(reinterpret_cast<uintptr_t>(&reinterpret_cast<point*>(16)->y) - static_cast<uintptr_t>(16u)
Еще один способ выразить reinterpret_cast<point*>(16)->y
можно было бы так: point * myPt = 16u;
мы знаем, что 16 не является допустимым адресом, но компилятор этого не делает, и пока мы не пытаемся прочитать адрес, на который мы указываем, все в порядке.
Далее мы можем упростить все &reinterpret_cast<point*>(16)->y
чтобы: &myPt->y
, Сверху мы знаем, что y равно @ offset 0x4, а myPt равно 16: 16 + 0x4 = 20
Тогда у нас есть reinterpret_cast<uintptr_t>(20u) - static_cast<uintptr_t(16u)
или же 20 - 16
, что дает нам смещение у, то есть 0x4.
Целое число 16 - это просто адрес памяти. выражение reinterpret_cast<t*>(16)
просто означает "интерпретировать объект по адресу 16 как тип t
", но вы знаете, что нет такого t
объект по адресу. Теоретически, 16
может быть заменено любым 4x (32-битным) или 8x (64-битным) целым числом. Если вы выбираете 0
Макрос может быть упрощен как:
#define OFFSETOF_MEMBER(t, f) \
(reinterpret_cast<uintptr_t>(&reinterpret_cast<t*>(0)->f))
См. Offsetof для получения дополнительной информации.
Из ссылки:
3) Значение любого целочисленного типа или типа перечисления может быть преобразовано в тип указателя. [...]
Так, reinterpret_cast<>
частично разработан, чтобы сделать именно это.