Более продвинутые говорят, что это зависит от имплементации, и приводят в пример какие-то embedded сценарии архитектур, до которых среднему программисту дела нет. Поэтому первая часть людей продолжает говорить, что в практическом поле nullptr - это ноль.
Но есть красивый пример, иллюстрирующий что это не так.
Природа заблуждения растет, как мне кажется, из проверки подстановкой указателя в if.
struct S {
char a;
char b;
int c;
int d;
};
int main()
{
auto s = S{'1', '2', 3, 4};
S* pS = nullptr;
if (!pS) {
std::cout << "pS" << std::endl;
std::cout << std::hex << "ptr: "<< *(int*)(&pS) << std::endl;
}
pS = &s;
if (pS) {
std::cout << "pS" << std::endl;
std::cout << std::hex << "ptr: "<< *(int*)(&pS) << std::endl;
}
}Ну тут все очевидно. Выведет вот это:
pS
ptr: 0
pS
ptr: 7ede1034
Указатель нулевой, и логично, что проверка
if (var) - это проверка на ноль, как мы и привыкли. Но помимо указателя на объект в С++ есть еще указатель на члены класса.
char S::* pSm = nullptr;
pSm = &S::a;
if (pSm) {
std::cout << "pS.a" << std::endl;
std::cout << std::dec << "value: " << s.*pSm << std::endl;
std::cout << std::hex << "ptr: "<< *(int*)(&pSm) << std::endl;
}
pSm = &S::b;
if (pSm) {
std::cout << "pS.b" << std::endl;
std::cout << std::dec << "value: " << s.*pSm << std::endl;
std::cout << std::hex << "ptr: "<< *(int*)(&pSm) << std::endl;
}
Что здесь важно. Указатель на член класса - это не адрес в памяти. Это смещение относительно адреса объекта к адресу члена. Таким образом, указатель на первый член класса всегда будет 0.
pS.a
value: 1
ptr: 0
pS.b
value: 2
ptr: 1
Еще более занятно, что прошла проверка
if (pSm) для нуля. Потому что это валидное и даже ожидаемое значение, а в бинарном коде эта проверка выглядит так:cmp rax, -1
Ну и тут можно догадаться, но мы выведем кодом ниже и чему равен nullptr для членов класса:
pSm = nullptr;
if (!pSm) {
std::cout << "pS.nullptr" << std::endl;
std::cout << std::dec << "value: " << s.*pSm << std::endl;
std::cout << std::hex << "ptr: "<< *(int*)(&pSm) << std::endl;
}
if (pSm) {
std::cout << "Unreacheable" << std::endl;
}
pS.nullptr
value:
ptr: ffffffff
значение nullptr - это минус один. И это без всяких эзотерических архитектур, в рамках одной и той же программы у нас есть два разных значения для nullptr.
Весело в С++ однако.....