Литмир - Электронная Библиотека
A
A

int* pi = new int;          // выделяем память для одной переменной int

int* qi = new int[4];       // выделяем память для четырех переменных int

                            // (массив)

double* pd = new double;    // выделяем память для одной переменной

                            // double

double* qd = new double[n]; // выделяем память для n переменных

                            // double

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

n
равно
2
, то произойдет следующее.

Программирование. Принципы и практика использования C++ Исправленное издание - _189.png

Указатели на объекты разных типов имеют разные типы. Рассмотрим пример.

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

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

Почему нельзя? В конце концов, мы же можем присвоить переменную типа

int
переменной типа
double
, и наоборот. Причина заключается в операторе
[]
. Для того чтобы найти элемент, он использует информацию о размере его типа. Например, элемент
qi[2]
находится на расстоянии, равном двум размерам типа
int
от элемента
qi[0]
, а элемент
qd[2]
находится на расстоянии, равном двум размерам типа
double
от элемента
qd[0]
. Если размер типа
int
отличается от размера типа
double
, как во многих компьютерах, то, разрешив указателю
qi
ссылаться на память, выделенную для адресации указателем
qd
, можем получить довольно странные результаты.

Это объяснение с практической точки зрения. С теоретической точки зрения ответ таков: присваивание друг другу указателей на разные типы сделало бы возможными ошибки типа (type errors).

17.4.2. Доступ с помощью указателей

Кроме оператора разыменования

*
, к указателю можно применять оператор индексирования
[]
. Рассмотрим пример.

double* p = new double[4]; // выделяем память для четырех переменных

                           // типа double в свободной памяти

double x = *p;             // читаем (первый) объект, на который

                           // ссылается p

double y = p[2];           // читаем третий объект, на который

                           // ссылается p

Так же как и в классе

vector
, оператор индексирования начинает отсчет от нуля. Это значит, что выражение
p[2]
ссылается на третий элемент;
p[0]
— это первый элемент, поэтому
p[0]
означает то же самое, что и
*p
. Операторы
[]
и
*
можно также использовать для записи.

*p = 7.7;   // записываем число в (первый) объект, на который

            // ссылается p

p[2] = 9.9; // записываем число в третий объект, на который

            // ссылается p

Указатель ссылается на объект, расположенный в памяти. Оператор разыменования (“contents of” operator, or dereference operator) позволяет читать и записывать объект, на который ссылается указатель

p
.

double x = *p; // читаем объект, на который ссылается указатель p

*p = 8.9;      // записываем объект, на который ссылается указатель p

Когда оператор

[]
применяется к указателю
p
, он интерпретирует память как последовательность объектов (имеющих тип, указанный в объявлении указателя), на первый из который ссылается указатель
p
.

double x = p[3]; // читаем четвертый объект, на который ссылается p

p[3] = 4.4;      // записываем четвертый объект, на который

                 // ссылается p

double y = p[0]; // p[0] - то же самое, что и *p

Вот и все. Здесь нет никаких проверок, никакой тонкой реализации — простой доступ к памяти.

Программирование. Принципы и практика использования C++ Исправленное издание - _190.png

Именно такой простой и оптимально эффективный механизм доступа к памяти нам нужен для реализации класса

vector
.

17.4.3. Диапазоны

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Основная проблема, связанная с указателями, заключается в том, что указатель не знает, на какое количество элементов он ссылается. Рассмотрим пример.

double* pd = new double[3];

pd[2] = 2.2;

pd[4] = 4.4;

pd[– 3] = – 3.3;

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Может ли указатель
pd
ссылаться на третий элемент
pd[2]
? Может ли он ссылаться на пятый элемент
pd[4]
? Если мы посмотрим на определение указателя
pd
, то ответим “да” и “нет” соответственно. Однако компилятор об этом не знает; он не отслеживает значения указателя. Наш код просто обращается к памяти так, будто она распределена правильно. Компилятор даже не возразит против выражения
pd[–3]
, как будто можно разместить три числа типа
double
перед элементом, на который ссылается указатель
pd
.

224
{"b":"847443","o":1}