Как и в случае с обычными переменными, указатели не инициализируются при создании. Если значение не было присвоено, то указатель по умолчанию будет указывать на любой адрес, содержимым которого является мусор.
Нулевое значение и нулевые указатели
Помимо адресов памяти, есть еще одно значение, которое указатель может хранить: значение null. Нулевое значение (или «значение null») — это специальное значение, которое означает, что указатель ни на что не указывает. Указатель, содержащий значение null, называется нулевым указателем.
В языке C++ мы можем присвоить указателю нулевое значение, инициализируя его/присваивая ему литерал 0
:
1 2 3 4 |
int *ptr(0); // ptr теперь нулевой указатель int *ptr1; // ptr1 не инициализирован ptr1 = 0; // ptr1 теперь нулевой указатель |
Поскольку значением нулевого указателя является нуль, то это можно использовать внутри условного ветвления для проверки того, является ли указатель нулевым или нет:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int main() { double *ptr(0); if (ptr) std::cout << "ptr is pointing to a double value."; else std::cout << "ptr is a null pointer."; return 0; } |
Совет: Инициализируйте указатели нулевым значением, если не собираетесь присваивать им другие значения.
Разыменование нулевых указателей
Как мы уже знаем из предыдущего урока, разыменование указателей с мусором приведет к неожиданным результатам. С разыменованием нулевого указателя дела обстоят так же. В большинстве случаев вы получите сбой в программе.
В этом есть смысл, ведь разыменование указателя означает, что нужно «перейти к адресу, на который указывает указатель, и достать из этого адреса значение». Нулевой указатель не имеет адреса, поэтому и такой результат.
Макрос NULL
В языке Cи (но не в C++) есть специальный макрос препроцессора с именем NULL, который определен как значение 0
. Хоть он и не является частью языка C++, его использование достаточно распространено, и должно работать в каждом компиляторе С++:
1 |
int *ptr(NULL); // присваиваем адрес 0 указателю ptr |
Однако, поскольку NULL является макросом препроцессора и, технически, не является частью C++, то его не рекомендуется использовать.
Ключевое слово nullptr в C++11
Обратите внимание, значение 0
не является типом указателя, и присваивание указателю значения 0
для обозначения того, что он является нулевым — немного противоречиво, вам не кажется? В редких случаях, использование 0
в качестве аргумента-литерала может привести к проблемам, так как компилятор не сможет определить, используется ли нулевой указатель или целое число 0
:
1 |
doAnything(0); // является ли 0 аргументом-значением или аргументом-указателем? (компилятор определит его как целочисленное значение) |
Для решения этой проблемы в C++11 ввели новое ключевое слово nullptr, которое также является константой r-value.
Начиная с C++11, при работе с нулевыми указателями, использование nullptr является более предпочтительным вариантом, нежели использование 0
:
1 |
int *ptr = nullptr; // примечание: ptr по-прежнему остается указателем типа int, просто со значением null (0) |
Язык C++ неявно преобразует nullptr в соответствующий тип указателя. Таким образом, в вышеприведенном примере, nullptr неявно преобразуется в указатель типа int, а затем значение nullptr
присваивается ptr
.
nullptr также может использоваться для вызова функции (в качестве аргумента-литерала):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> void doAnything(int *ptr) { if (ptr) std::cout << "You passed in " << *ptr << '\n'; else std::cout << "You passed in a null pointer\n"; } int main() { doAnything(nullptr); // теперь аргумент является точно нулевым указателем, а не целочисленным значением return 0; } |
Совет: В C++11 используйте nullptr для инициализации нулевых указателей.
Тип данных std::nullptr_t в C++11
В C++11 добавили новый тип данных std::nullptr_t, который находится в заголовочном файле cstddef. std::nullptr_t может иметь только одно значение — nullptr
! Хотя это может показаться немного глупым, но это полезно в одном случае. Если вам нужно написать функцию, которая принимает аргумент nullptr, то какой тип параметра нужно использовать? Правильно! std::nullptr_t. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> #include <cstddef> // для std::nullptr_t void doAnything(std::nullptr_t ptr) { std::cout << "in doAnything()\n"; } int main() { doAnything(nullptr); // вызов функции doAnything() с аргументом типа std::nullptr_t return 0; } |
Вам, вероятно, никогда это не придется использовать, но знать об этом стоит (на всякий пожарный).
с этими указателями….какие-то скороговорки для мозга..
Так и не понял, для чего нужен std::nullptr_t. Зачем его ввели.
Судя по тому, что я узнал за 81 урок и какое мнение сложилось о С++, ответ на Ваш вопрос: "просто так, на всякий пожарный"))))))
ps. 3 допустимых вида инициализации переменных, значит, вас не смущают?)))))
В этих уроках объясняется как база, так и нюансы, которые вы, скорее всего, не очень часто будете использовать на практике, но знать об этом стоит.
Каждая переменная или константа должна быть определенного типа.
Следовательно, у nullptr (как константы, а не ключевого слова) тоже должен быть какой-то тип.
В MSDN есть пример, показывающий зачем ввели дополнительный тип:
Например, если для функции func(std::pair<const char *, double>) произвести вызов func(std::make_pair(NULL, 3.14)), возникнет ошибка времени компиляции. Макрос NULL разворачивается в 0, поэтому вызов std::make_pair(0, 3.14) возвращает значение std::pair<int, double>, которое невозможно преобразовать к типу параметра std::pair<const char *, double> функции func(). Вызов func(std::make_pair(nullptr, 3.14)) успешно компилируется, поскольку std::make_pair(nullptr, 3.14) возвращает значение std::pair<std::nullptr_t, double>, которое допускает преобразование в тип std::pair<const char *, double>.
>> https://docs.microsoft.com/ru-ru/cpp/cpp/nullptr
>> https://msdn.microsoft.com/ru-ru/windows/desktop/jj651642
Зачем еще оно нужно:
Приходите на собеседование, и дают вам такой код 😉
Из http://www.cyberforum.ru/cpp-beginners/thread638684.html#post3365236
Ответ: http://ideone.com/Av14Ae
Зачем он нужен — понятно (см. конец). Непонятно — зачем кому-то писать функцию, принимающую этот nullptr как параметр? Он же не несёт никакую информацию в функцию.
Вроде я допёхал. Он нужен тупо для вызова функции без каких либо значений. Ну к примеру для проги выше нам надо объявит переменную, затем присвоить ей значение и только после этого засунуть её в функцию. Примерно так:
То есть + 2 строчки из которых 1 указатель, а второй переменная, а так мы тупо без каких либо объявлений пихаем эти значения и функция выполняется. Как я понимаю используется в чём то типо кнопок.
Думаю, что это можно использовать с целью производительности для особого поведения при передаче параметра nullptr в метод класса, просто перегружаете метод, который принимает, например, строку и некий необязательный указатель.
Грубый пример:
Это позволит не выполнять лишних действий если указатель=nullptr.
p.s. Если это вам не понятно, то дочитайте эти уроки до конца и вернитесь сюда.
Можете добавить примеры с применением нулевых указателей, в там числе с передачей тех в функцию как аргумент?