- •Глава 7. Указатели
- •Основные понятия
- •Объявление указателей
- •Операция адреса
- •Операция разыменования
- •Инициализация указателей
- •Адресная арифметика
- •Функции и указатели
- •Передача аргументов в функцию по указателю
- •Указатель в качестве возвращаемого значения функции
- •Некоторые особенности использования указателей Нулевые указатели
- •Указатели на тип void
- •Указатели на константу и константные указатели
Инициализация указателей
Как и другие переменные, помимо объявления, указатели требуют инициализации, так как в противном случае они содержат непредсказуемые значения. Существуют следующие способы инициализации указателя:
присваивание указателю адреса существующего объекта (переменной, массива и др.) с помощью операции адреса (&):
int a = 5;
int * p = &a; или int * p(&a);
присваивание указателю пустого значения:
int *p = NULL; или int * p = 0
с помощью значения другого инициализирующего указателя:
int * p1 = p; или int * p1(p);
c помощью имени массива:
int b[10]; int *p = b; (cм. главу 8)
присваивание адреса динамической памяти:
int *p = new int; (cм. главу 9)
Адресная арифметика
Под адресной арифметикой (или арифметикой над указателями) понимаются действия над указателями, связанные с использованием адресов памяти. Рассмотрим основные операции, которые можно применять к указателям, а какие – нельзя.
Операция адреса
Так как указатель – это переменная, то для получения адреса памяти, где расположен указатель, можно использовать операцию адреса (&):
int * p, x = 5; p = &x;
cout<<&x<<&p<<endl;
Операция присваивания
Указателю можно присвоить адрес переменной
p = &x, где p – указатель, x – имя переменной.
Указатели можно присваивать один другому, если оба указателя одного типа:
int x = 5; int * p = &x;
int * q = p;
cout<<*p<<' '<<*q<<endl; // 5 5
Операция разыменования
Определение значения, на которое ссылается указатель p:
int * p, n, m = 5;
p = &m; // p присвоен адрес переменной m
n = * p; // переменная n примет значение 5
*p = -13; // переменная m примет значение -13
Увеличение/уменьшение указателя
Основными арифметическими операциями, применимыми к указателям, являются операции увеличения и уменьшения указателя:
имя_указателя + i и имя_указателя – i, где
имя_указателя – переменная типа указатель; i – значение типа int.
Результатом операции имя_указателя + i является указатель на i-й элемент после элемента, адрес которого находится в данном указателе, а имя_указателя - i – указатель на i-й элемент до элемента, адрес которого находится в данном указателе.
double d,*p; int n;
p = &d;
n = 3; p = p + n; // в p адрес увеличился на 24
p =n + p; // в p адрес увеличился ещё на 24
Если к указателю применяется операция инкремента (++) или операция декремента (--), то значение указателя увеличивается или уменьшается на размер данных, которые он адресует.
long b;
long * p; // p – указатель на тип long
p = &b; // в p адрес переменной b
p++; // в p адрес увеличится на 4
p--; // в p адрес уменьшится на 4
Вычитание указателей
При работе с указателями можно использовать операцию вычитания двух указателей:
имя_указателя1 – имя_указателя2
Операция имеет смысл, только если обе переменные являются указателями на один и тот же набор данных (например, массив). Результатом операции является целое число, которое показывает сколько элементов соответствующего типа можно расположить между адресами памяти, на которые указывают указатели имя_указателя1, имя_указателя2.
Операции сравнения
Указатели на данные одного и того же типа можно сравнивать с помощью обычных операций сравнения: ==, !=, >, >=, <, <=.
При сравнении указателей сравниваются их значения (хранимые в них адреса), а не значения величин, на которые данные указатели указывают. Результатом операций сравнения является целое число (0 или 1).
Сравнение указателей с числовыми значениями как сравнение данных разных типов не определено.
Операция sizeof
Для определения, сколько байтов отводится под указатель, можно использовать операцию sizeof(указатель). Количество байтов зависит от модели памяти (обычно 4 байта).
Недопустимые операции
К указателям можно применять только описанные операции и операции, которые выражаются через них, например, разрешается к указателю применить операцию uk += n; , так как ее можно выразить через uk = uk+n; . Следующие операции недопустимы с указателями:
сложение двух указателей;
вычитание двух указателей на данные различных типов;
сложение/вычитание указателей с числом с плавающей точкой;
умножение/деление указателей;
поразрядные операции и операции сдвига.
При записи выражений с указателями следует обращать внимание на приоритеты операций. В качестве примера рассмотрим последовательность действий, заданную в операторе
*p++ = 10;
Операции разыменования и инкремента имеют одинаковый приоритет и выполняются справа налево, но поскольку инкремент постфиксный, он выполняется после операции приcваивания. Таким образом, сначала по адресу, записанному в указателе p, будет записано значение 10, а затем указатель будет увеличен на количество байтов, соответствующее его типу.
Оператор (*p)++ напротив, увеличивает на единицу значение, на которое ссылается указатель p.