Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 16 17 18 19 20 21 22 23 24 ... 297
Перейти на страницу:

p = &i;       // & используется в выражении как оператор

              // обращения к адресу

*p = i;       // * используется в выражении как оператор

              // обращения к значению

int &r2 = *p; // & в части объявления; * - оператор обращения к значению

В объявлениях символы & и * используются для формирования составных типов. В выражениях эти же символы используются для обозначения оператора. Поскольку тот же символ используется в совершенно ином смысле, возможно, стоит игнорировать внешнее сходство и считать их как будто различными символами.

Нулевые указатели

Нулевой указатель (null pointer) не указывает ни на какой объект. Код может проверить, не является ли указатель нулевым, прежде чем пытаться использовать его. Есть несколько способов получить нулевой указатель.

int *p1 = nullptr; // эквивалентно int *p1 = 0;

int *p2 = 0;       // непосредственно инициализирует p2 литеральной

                   // константой 0, необходимо #include cstdlib

int *p3 = NULL;    // эквивалентно int *p3 = 0;

Проще всего инициализировать указатель, используя литерал nullptr, который был введен новым стандартом. Литерал nullptr имеет специальный тип, который может быть преобразован (см. раздел 2.1.2) в любой другой ссылочный тип. В качестве альтернативы можно инициализировать указатель литералом 0, как это сделано в определении указателя p2.

Программисты со стажем иногда используют переменную препроцессора (preprocessor variable) NULL, которую заголовок cstdlib определяет как 0.

Немного подробней препроцессор рассматривается в разделе 2.6.3, а пока достаточно знать, что препроцессор (preprocessor) — это программа, которая выполняется перед компилятором. Переменные препроцессора используются препроцессором, они не являются частью пространства имен std, поэтому их указывают непосредственно, без префикса std::.

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

Нельзя присваивать переменную типа int указателю, даже если ее значением является 0.

int zero = 0;

pi = zero; // ошибка: нельзя присвоить переменную типа int указателю

Совет. Инициализируйте все указатели

Неинициализированные указатели — обычный источник ошибок времени выполнения.

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

У большинства компиляторов при использовании неинициализированного указателя биты в памяти, где он располагается, используются как адрес. Использование неинициализированного указателя — это попытка доступа к несуществующему объекту в произвольной области памяти. Нет никакого способа отличить допустимый адрес от недопустимого, состоящего из случайных битов, находящихся в той области памяти, которая была зарезервирована для указателя.

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

Присвоение и указатели

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

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

int i = 42;

int *pi = 0;   // указатель pi инициализирован, но не адресом объекта

int *pi2 = &i; // указатель pi2 инициализирован адресом объекта i

int *pi3;      // если pi3 определен в блоке, pi3 не инициализирован

pi3 = pi2;     // pi3 и pi2 указывают на тот же объект, т.е. на i

pi2 = 0;       // теперь pi2 не содержит адреса никакого объекта

Сначала может быть трудно понять, изменяет ли присвоение указатель или сам объект, на который он указывает. Важно не забывать, что присвоение изменяет свой левый операнд. Следующий код присваивает новое значение переменной pi, что изменяет адрес, который она хранит:

pi = &ival; // значение pi изменено; теперь pi указывает на ival

С другой стороны, следующий код (использующий *pi, т.е. значение, на которое указывает указатель pi) изменяет значение объекта:

*pi = 0; // значение ival изменено; pi неизменен

Другие операции с указателями

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

int ival = 1024;

int *pi = 0;      // pi допустим, нулевой указатель

int *pi2 = &ival; // pi2 допустим, содержит адрес ival

if (pi)           // pi содержит значение 0, условие считается ложным

 // ...

if (pi2)          // pi2 указывает на ival, значит, содержит не 0;

                  // условие считается истинным

 // ...

Любой отличный от нулевого указатель рассматривается как значение true. Два допустимых указателя того же типа можно сравнить, используя операторы равенства (==) и неравенства (!=). Результат этих операторов имеет тип bool. Два указателя равны, если они содержат одинаковый адрес, и неравны в противном случае. Два указателя содержат одинаковый адрес (т.е. равны), если они оба нулевые, если они указывают на тот же объект или на область непосредственно за концом того же объекта. Обратите внимание, что указатель на объект и указатель на область за концом другого объекта вполне могут содержать одинаковый адрес. Такие указатели равны.

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

Дополнительные операции с указателями будут описаны в разделе 3.5.3.

Тип void* является специальным типом указателя, способного содержать адрес любого объекта. Подобно любому другому указателю, указатель void* содержит адрес, но тип объекта по этому адресу неизвестен.

double obj = 3.14, *pd = &obj;

// ok: void* может содержать адрес любого типа данных

void *pv = &obj; // obj может быть объектом любого типа

pv = pd;         // pv может содержать указатель на любой тип

С указателем void* допустимо немного действий: его можно сравнить с другим указателем, можно передать его функции или возвратить из нее либо присвоить другому указателю типа void*. Его нельзя использовать для работы с объектом, адрес которого он содержит, поскольку неизвестен тип объекта, неизвестны и операции, которые можно с ним выполнять.

Как правило, указатель void* используют для работы с памятью как с областью памяти, а не для доступа к объекту, хранящемуся в этой области. Использование указателей void* рассматривается в разделе 19.1.1, а в разделе 4.11.3 продемонстрировано, как можно получить адрес, хранящийся в указателе void*.

Упражнения раздела 2.3.2

Упражнение 2.18. Напишите код, изменяющий значение указателя. Напишите код для изменения значения, на которое указывает указатель.

Упражнение 2.19. Объясните основные отличия между указателями и ссылками.

1 ... 16 17 18 19 20 21 22 23 24 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии