Как указатель сделать нулевым

Добавил пользователь Алексей Ф.
Обновлено: 19.09.2024

Теперь у нас есть C ++ 11 со многими новыми функциями. Интересным и запутанным (по крайней мере для меня) является новое nullptr .

Ну, не надо больше для мерзкого макроса NULL .

Тем не менее, я не понимаю, как nullptr работает. Например, статья в Википедии гласит:

C ++ 11 исправляет это, вводя новое ключевое слово, служащее отличительной константой нулевого указателя: nullptr. Он имеет тип nullptr_t , который неявно конвертируем и сопоставим с любым типом указателя или указателем на член. Он не является неявно конвертируемым или сопоставимым с целочисленными типами, за исключением bool.

Как это ключевое слово и экземпляр типа?

Кроме того, у вас есть еще один пример (помимо Википедии), где nullptr он превосходит старый добрый 0 ?

Связанный факт: nullptr также используется для представления нулевой ссылки для управляемых дескрипторов в C ++ / CLI.

При использовании Visual C ++ помните, что если вы используете nullptr с собственным кодом C / C ++, а затем компилируете с параметром компилятора / clr, компилятор не может определить, указывает ли nullptr собственное или управляемое значение нулевого указателя. Чтобы сделать ваше намерение понятным для компилятора, используйте nullptr для указания управляемого значения или __nullptr для указания собственного значения. Microsoft реализовала это как расширение компонента.

Является ли nullptr_t гарантировано иметь только один элемент, nullptr ? Итак, если функция вернулась nullptr_t , то компилятор уже знает, какое значение будет возвращено, независимо от тела функции?

std::nullptr_t Можно создать экземпляр @AaronMcDaid , но все экземпляры будут идентичны, nullptr поскольку тип определяется как typedef decltype(nullptr) nullptr_t . Я считаю, что основная причина существования типа заключается в том, что функции могут быть перегружены специально для перехвата nullptr , если это необходимо. Смотрите здесь для примера.

0 никогда не был нулевым указателем, нулевой указатель - это указатель, который можно получить, приведя нулевой литерал к типу указателя, и он не указывает ни на какой существующий объект по определению.

Как это ключевое слово и экземпляр типа?

Это не удивительно. Оба true и false являются ключевыми словами и в качестве литералов они имеют тип ( bool ). nullptr является литералом-указателем типа std::nullptr_t , и это значение (вы не можете получить его адрес, используя & ).

4.10 О преобразовании указателя говорит, что значение типа std::nullptr_t является константой нулевого указателя и что целая константа нулевого указателя может быть преобразована в std::nullptr_t . Противоположное направление не допускается. Это позволяет перегрузить функцию как для указателей, так и для целых чисел, и передать ее nullptr для выбора версии указателя. Передача NULL или 0 будет сбивать с толку выбрал int версию.

Приведение nullptr_t к целочисленному типу требует reinterpret_cast и имеет ту же семантику, что и приведение (void*)0 к целочисленному типу (определена реализация отображения). Не reinterpret_cast может преобразовать nullptr_t в любой тип указателя. Положитесь на неявное преобразование, если это возможно, или используйте static_cast .

Стандарт требует, чтобы это sizeof(nullptr_t) было sizeof(void*) .

О, после просмотра мне кажется, что условный оператор не может преобразовать 0 в nullptr в таких случаях, как cond ? nullptr : 0; . Удалено из моего ответа.

Обратите внимание, что NULL это даже не гарантируется 0 . Может быть 0L , в этом случае вызов void f(int); void f(char *); будет неоднозначным. nullptr всегда будет поддерживать версию указателя и никогда не вызывать ее int . Также обратите внимание , что nullptr это конвертируется в bool (проект говорит , что в 4.12 ).

@ Стив, нет, это вызовет int версию. Но f(0L) это неоднозначно, потому long -> int что long -> void* так же, как и одинаково дорого. Так что, если 0L в вашем компиляторе есть NULL , то вызов f(NULL) будет двусмысленным, учитывая эти две функции. Конечно, не так nullptr .

@SvenS Он не должен быть определен как (void*)0 в C ++. Но его можно определить как любую произвольную константу нулевого указателя, которую любая целочисленная константа со значением 0 и nullptr выполнит. Итак, определенно нет будет, но может . (Вы забыли пинговать меня между прочим ..)

    , с примером кода.
  • Здесь, в переполнении стека: вы используете NULL или 0 (ноль) для указателей в C ++?
  • Группа Google: comp.lang.c ++. Moderated - обсуждение компилятора

Почему nullptr в C ++ 11? Что это? Почему NULL недостаточно?

Эксперт по С ++ Алекс Аллен прекрасно говорит здесь (мой акцент выделен жирным шрифтом):

. представьте, что у вас есть следующие два объявления функций:

Хотя, похоже, будет вызвана вторая функция - в конце концов, вы передаете то, что кажется указателем - это действительно первая функция, которая будет вызвана! Проблема в том, что, поскольку NULL равен 0, а 0 - целое число, вместо него будет вызвана первая версия func. Это такая вещь, которая, да, не случается все время, но когда это происходит, это чрезвычайно расстраивает и сбивает с толку. Если вы не знаете подробностей происходящего, это может выглядеть как ошибка компилятора. Функция языка, которая выглядит как ошибка компилятора, ну, не то, что вам нужно.

Введите nullptr. В C ++ 11 nullptr - это новое ключевое слово, которое можно (и нужно!) Использовать для представления указателей NULL; другими словами, где бы вы ни писали ранее NULL, вы должны использовать вместо него nullptr. Это не более понятно для вас, программист , (все знают, что означает NULL), но это более явно для компилятора , который больше не будет видеть везде, где используются 0, чтобы иметь особое значение при использовании в качестве указателя.

Аллен заканчивает свою статью:

Независимо от всего этого - практическое правило для C ++ 11 - просто начинать использовать nullptr всякий раз, когда вы в противном случае использовали бы его NULL в прошлом.

Наконец, не забывайте, что nullptr это объект - класс. Он может быть использован в любом месте NULL использовалась раньше, но если вам нужно его тип по какой - то причине, это тип можно экстрагировать decltype(nullptr) или непосредственно , как описано std::nullptr_t , что это просто typedef из decltype(nullptr) .

Указатель — переменная, содержащая адрес объекта. Указатель не несет информации о содержимом объекта, а содержит сведения о том, где размещен объект.

Указатели похожи на метки, которые ссылаются на места в памяти. Они тоже имеют адрес, а их значение является адресом некоторой другой переменной. Переменная, объявленная как указатель, занимает 4 байта в оперативной памяти (в случае 32-битной версии компилятора).

Синтаксис указателей

Тип указателя— это тип переменной, адрес которой он содержит. Для работы с указателями в Си определены две операции:

  • операция * (звездочка) — позволяет получить значение объекта по его адресу – определяет значение переменной, которое содержится по адресу, содержащемуся в указателе;
  • операция & (амперсанд) — позволяет определить адрес переменной.

Объявление указателя, получение адреса переменной

Указатель на указатель

Указатель хранит адрес области памяти. Можно создать указатель на указатель, тогда он будет хранить адрес указателя и сможет обращаться к его содержимому. Указатель на указатель определяется как:

Пример работы указателя на указатель:

Указатели и приведение типов

Так как указатель хранит адрес, можно кастовать его до другого типа. Это может понадобиться, если нужно взять часть переменной, или если мы знаем, что переменная хранит нужный нам тип.

В следующем примере мы пользуемся тем, что размер типа int равен 4 байта, а char 1 байт. За счёт этого, получив адрес первого байта, можно пройти по остальным байтам числа и вывести их содержимое.

NULL pointer – нулевой указатель

Указатель до инициализации хранит мусор, как и любая другая переменная. Но в, то, же время, этот “мусор” вполне может оказаться валидным адресом. Например, есть указатель. Каким образом узнать, инициализирован он или нет? В общем случае никак. Для решения этой проблемы был введён макрос NULL библиотеки stdlib.

Принято при определении указателя, если он не инициализируется конкретным значением, делать его равным NULL.

По стандарту гарантировано, что в этом случае указатель равен NULL, и равен нулю, и может быть использован как булево значение false. Хотя в зависимости от реализации NULL может и не быть равным 0. То есть указатель можно сравнивать с нулём, или с NULL, но нельзя NULL сравнивать с переменной целого типа или типа с плавающей точкой.

Указатели. Часть 2. Неуправляемые указатели. Операции над указателями. Указатель на тип void . Выделение памяти. Нулевой указатель. Операция взятия адреса &

В данной теме описывается работа с неуправляемыми указателями. Как известно, Visual C++ поддерживает также и управляемые указатели .

Общие сведения об указателях, типах указателей подробно изложены в теме здесь .

Содержание

  • 1. Какие операторы можно использовать над неуправляемыми указателями?
  • 2. Что такое операция косвенного доступа с помощью указателя?
  • 3. Каким образом компилятор C++ определяет объем обрабатываемой информации, на которую указывает указатель?
  • 4. Операция присваивания указателей. Пример
  • 5. Арифметические операции над указателями. Изменение физического адреса указателя. Примеры
  • 6. Какие операции отношения (сравнения) можно выполнять над указателями?
  • 7. Какие особенности использования указателя на тип void ? Пример
  • 8. Каким образом указателю присваивается нулевое значение? Пример
  • 9. Пример выделения памяти функцией malloc() , которая возвращает указатель на тип void .
  • 10. Какие ограничения накладываются на операцию взятия адреса & ?
  • 11. Можно ли управляемому указателю присваивать непосредственно физический адрес памяти?
  • Связанные темы

Поиск на других ресурсах:

1. Какие операторы можно использовать над неуправляемыми указателями?

Над неуправляемыми указателями используются два оператора:

  • * – предназначен для обращения к переменной, которая размещается по адресу, который задается операндом этого оператора. Этот оператор ссылается на значение переменной, которая записана в указателе. Это унарный оператор, в котором операнд размещается справа.
  • & – возвращает адрес памяти операнда, размещающегося справа. Это унарный оператор.

Операторы * и & дополняют друг друга.

Например. Указатель с именем px ссылается на переменную x вещественного типа. Пусть имеется следующее описание:

это значение *px есть значением переменной x , что демонстрирует следующая строка

Значение px есть адресом переменной x в оперативной памяти.

Рисунок 1. Указатель px указывает на переменную x

2. Что такое операция косвенного доступа с помощью указателя?

Операция косвенного доступа – это доступ к переменной (объекту) с использованием указателя. С помощью указателя можно получать доступ к массивам переменных (объектов).

3. Каким образом компилятор C++ определяет объем обрабатываемой информации, на которую указывает указатель?

Объем обрабатываемой информации определяется базовым типом указателя.

Пример. Пусть дано следующее описание:

базовый тип указателя есть double . Одна переменная типа double занимает в памяти компьютера 8 байт. Поэтому компилятор C++ будет обрабатывать 8 байт информации при доступе к ней по указателю.

4. Операция присваивания указателей. Пример

Значение одного указателя можно присваивать другому при условии, что они имеют одинаковый базовый тип (есть указателями на одинаковый тип данных).

Если указатели указывают на разные типы данных, то работает операция приведения типов . В этом случае работа с указателем есть непредсказуемой и может привести к невидимым логическим ошибкам (а может и нет).

Пример.

5. Арифметические операции над указателями. Изменение физического адреса указателя. Примеры

Над указателями можно выполнять следующие арифметические операции:

  • операции инкремента ++ и декремента — ;
  • операции сложения + и вычитания – .

Указатели могут принимать участие в выражениях.

При изменении значения указателя с помощью арифметических операций физический адрес указателя изменяется по формуле:

  • N – константа, которая указывает изменение (увеличение/уменьшение) значения указателя;
  • тип – тип данных, на который указывает указатель (базовый тип указателя);
  • sizeof(тип) – операция, которая определяет размер типа данных.

Пример 1. Изменение значения указателя.

В вышеприведенном примере физический адрес указателя pi2 больше на 4 байта от физического адреса указателя pi1 . Это связано с тем, что тип int (базовый тип указателей pi1 и pi2 ) имеет размер 4 байта (среда Win32 ).

Пример 2. Изменение значения указателя на тип double .

6. Какие операции отношения (сравнения) можно выполнять над указателями?

Указатели можно сравнивать. Для сравнения указателей используются операции == , > , .

При сравнении указателей важно, чтобы эти указатели имели некоторую связь между собой.

Например, если указатели указывают на разные ничем не связанные переменные, то операция сравнения не имеет смысла.

В случае, если указатели указывают на переменные, между которыми есть некоторая связь, то результат сравнения имеет смысл. Это может быть случай, когда указатели указывают на элементы одного и того же массива.

Пример. Пусть дан массив вещественных чисел A и 2 указателя.

7. Какие особенности использования указателя на тип void ? Пример

Язык C/C++ позволяет использовать указатель на тип void .

Указатель на тип void имеет свои особенности. Это означает, что указатель на тип void есть универсальным указателем, который может настраиваться на любой тип значений, в том числе и нулевой. Ключевое слово void дает компилятору информацию, что отсутствуют данные о размере объекта в памяти.

Чтобы привести указатель на void к указателю на другой тип нужно использовать операцию приведения типов.

Пример. Использование указателя на тип void для доступа к переменным разных типов.

8. Каким образом указателю присваивается нулевое значение? Пример

Бывают случаи, когда указателю нужно присвоить нулевое значение. Это нужно, чтобы установить значение указателя таким, которое не совпадает ни с одним из указателей, которые используются в программе.

В модуле определена константа NULL с нулевым значением. Ее можно использовать для присвоения нулевого значения указателю.

Пример. Присвоение нулевого значения указателю.

9. Пример выделения памяти функцией malloc() , которая возвращает указатель на тип void .

Функция malloc() выделяет память для неуправляемого указателя. Функция возвращает тип void * .

Чтобы присвоить указателю значения адреса памяти, выделенной для объекта, нужно выполнить явное приведение типа.

Пример. Использование функции malloc() .

10. Какие ограничения накладываются на операцию взятия адреса & ?

На операцию взятия адреса & накладываются следующие ограничения:

1. Невозможно определить адрес константы. Например, выражение

приведет к ошибке.

2. Невозможно определить адрес значения, которое получается при вычислении выражения. Например, фрагмент кода

приведет к ошибке.

11. Можно ли управляемому указателю присваивать непосредственно физический адрес памяти?

Это означает, что состоялась попытка доступа к защищенной памяти.

Это касается как консольных приложений, так и приложений, созданных по шаблону Windows Forms Application .

в чем разница между a нулевой указатель и пустой указатель?

нулевой указатель является специальным зарезервированным стоимостью указателя. Указатель любого типа имеет такое зарезервированное значение. Формально, каждый конкретный тип указателя ( int * , char * etc.) имеет свое собственное выделенное значение нулевого указателя. Концептуально, когда указатель имеет это нулевое значение, он никуда не указывает.

пустой указатель - это конкретный указатель тип - void * - указатель, указывающий на некоторое место данных в хранилище, которое не имеет никакого конкретного типа.

Итак, еще раз нулевой указатель это стоимостью, а пустой указатель это тип. Эти понятия совершенно разные и несопоставимые. Это по существу означает, что ваш вопрос, как указано, не совсем корректен. Это все равно что спросить, например: "в чем разница между треугольником и автомобилем?".

Это два разных понятия:" указатель void " - это тип (void *). "нулевой указатель" - это указатель, имеющий нулевое значение (NULL). Пример:

это указатель NULL void.

нулевой указатель гарантированно не будет сравниваться с указателем на любой объект. Это фактическое значение зависит от системы и может варьироваться в зависимости от типа. Чтобы получить null int указатель, вы могли бы сделать

нулевой указатель будет возвращен malloc на провал.

мы можем проверить, если указатель равен null, т. е. если malloc или какая-то другая функция не удалось просто проверить его логическое значение:

пустой указатель может указывать на любой тип и до вас, чтобы обработать, сколько памяти ссылочные объекты потребляют для разыменования и арифметики указателя.

Void относится к типу. В основном тип данных, на которые он указывает, неизвестен.

Null относится к значению. Это по сути указатель на ничто, и недопустимо использовать.

нулевой указатель имеет значение NULL который обычно равен 0, но в любом случае является ячейкой памяти, которая недопустима для разыменования. Пустой указатель указывает на данные типа Void. Слово "void" не указывает на то, что данные, на которые ссылается указатель, являются недопустимыми или что указатель был обнулен.

  • адрес 0, против которого большинство наборов инструкций CPU может выполнять очень быстрое сравнение и ветвление (например, для проверки неинициализированных или недопустимых указателей) с оптимальным размером кода/производительностью для ISA.

  • адрес, который является незаконным для пользовательского кода для доступа (например, 0x00000000 во многих случаях), так что если код действительно пытается получить доступ данные по этому адресу или рядом с ним ОС или отладчик могут легко остановить или перехватить программу с этой ошибкой.

NULL - Это значение, допустимое для любого типа указателя. Он представляет собой отсутствие значения.

указатель void-это тип. Любой тип указателя преобразуется в указатель void, поэтому он может указывать на любое значение. Это делает его хорошим для общего хранения, но плохо для использования. Сам по себе он не может быть использован для доступа к значению. Программа должна иметь дополнительный контекст, чтобы понять тип значения, на которое ссылается указатель void, прежде чем он сможет получить доступ к значению.

указатели Null и указатели void полностью отличаются друг от друга. Если мы запрашиваем операционную систему (через malloc () в C langauge) выделить память для определенного типа данных, то операционная система выделяет память в куче (если пространство доступно в куче) и отправляет адрес памяти, которая была выделена.

когда память выделяется ОС в куче, мы можем назначить это значение адреса в любой переменной типа указателя этого типа данных. Этот указатель является затем вызывается указатель void, пока он не будет принят для любого процесса.

когда пространство недоступно в куче, операционная система, безусловно, выделяет память и отправляет значение адреса этого местоположения,но эта память не выделяется в куче ОС, потому что в куче нет места, в этом случае эта память выделяется ОС в системной памяти.. Эта память не может быть доступна пользователю, следовательно, когда мы назначаем это значение адреса в указателе, то этот указатель известный как нулевой указатель, и мы не можем использовать этот указатель. В случае пустой указатель можно использовать для любого процесса на любом языке программирования.

  1. NULL Это просто константа указателя, иначе как бы мы могли ptr = NULL .
  2. как NULL это указатель, каков его тип. Я думаю, что тип просто (void *) , иначе как бы мы оба int * ptr = NULL и (user-defined type)* ptr = NULL . void тип на самом деле универсального типа.
  3. цитируется в "C11 (ISO/IEC 9899:201x) §6.3.2.3 указатели раздел 3":

    целочисленная константа выражение со значением 0, или такое выражение приводится к типу Void *, называется нулевой указатель константа

Итак, проще говоря: NULL указатель-это константа указателя void.

нулевой указатель - это указатель, который ни на что не указывает, он используется в тех случаях, когда указателю не назначен действительный адрес в памяти. Каждый тип указателя, т. е. int *, char *, имеет собственное значение нулевого указателя.

A пустой указатель это тот, который не имеет никакого типа данных, связанных с ним, т. е. ему может быть присвоено значение любого типа. Также известный как указатель общего назначения, это соглашение C для raw адрес.

таким образом, нулевой указатель-это в основном нулевое значение, назначенное указателю любого типа данных, тогда как указатель void-это тип данных, который остается пустым до тех пор, пока ему не назначен адрес типа данных.

Читайте также: