TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #924 4.97K
​​WAT
#опытным

Спасибо, @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
// true


nullptr указатель равен совсем не нулю, как декларировалось в начале 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
  • ❤ 25
  • 👍 12
  • 🔥 9
  • 🤯 4
  • ❤‍🔥 2
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →