#опытным
Спасибо, @Ivaneo, за любезно предоставленный примерчик в рамках рубрики #ЧЗХ.
Всегда ли nullptr указатель равен нулю?
Казалось бы в названии дан ответ:
int * p = nullptr;
std::cout << std::boolalpha << (p == nullptr) << "\n";
std::cout << std::hex << std::bit_cast<std::uintptr_t>(p) << "\n";
// OUTPUT
// true
// 0
Но в общем случае это неправда! Смотрим на пример:
struct A {
int i;
};
int main() {
int A::* p = 0;
std::cout << std::boolalpha << (p == nullptr) << "\n";
std::cout << std::hex << std::bit_cast<std::uintptr_t>(p) << "\n";
std::cout << std::boolalpha << (std::bit_cast<std::uintptr_t>(p) == 0xffffffffffffffff) << "\n";
}
// OUTPUT:
// true
// ffffffffffffffff
// truenullptr указатель равен совсем не нулю, как декларировалось в начале main.
WAT? Что за фокусы с пропажей нуля?
Во вчерашнем посте мы рассказывали об особом типе указателя - pointer to data member. Этот указатель, которым и является
p из примера, по сути хранит информацию о том, как в объекте найти нужное поле класса.И в большинстве случаев эта информация представляет собой просто смещение поля относительно начала объекта в байтах.
Однако нулевое смещение используется для локации самого первого поля класса. Поэтому в байтовом представлении неинициализированный указатель не может быть нулем.
Вместо этого обычно используется число -1, которое в байтовом представлении как раз выглядит как все единички:
std::cout << std::hex << static_cast<long long int>(-1) << "\n";
// OUTPUT:
// ffffffffffffffff
С помощью указателей на поля класса можно кстати наглядно изучать выравнивание и упаковку полей с объект:
struct Type {
double a;
char b;
float c;
long long d;
short e;
unsigned f;
};
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::a) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::b) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::c) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::d) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::e) << "\n";
std::cout << std::dec << std::bit_cast<std::uintptr_t>(&Type::f) << "\n";
// OUTPUT:
// 0
// 8
// 12
// 16
// 24
// 28Опять же, интересный уголок плюсов.
Walk through the nooks and crannies. Stay cool.
#cppcore #memory