ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4243
Скачиваний: 28
Часть I. Язык программирования С
66
int s = 0;
if (!s)
goto err;
++s;
err:--s; /* s = -1 */
В настоящее время в программировании инструкция
goto
приме-
няется крайне редко (и не рекомендуется к использованию), а для
обработки ошибки во вложенных блоках используется механизм
обработки исключений.
Г Л А В А
5
Указатели и массивы
5.1. Указатели
Указатель
— это переменная или именованная константа, кото-
рая содержит адрес (в памяти) другой переменной, именованной
константы или функции. В этом разделе будут рассмотрены
только указатели на переменные и именованные константы. Для
объявления указателя нужно перед именем переменной, которая
объявляется как указатель, поставить символ
*
. В этом случае
переменная является указателем на данные, тип которых задан
спецификатором типа. Например:
int *ip; /* указатель на целое число */
char *cp; /* указатель на символ */
double *dp; /* указатель на плавающее число */
Часто при объявлении указателей символ
*
ставят не перед пере-
менной, а после типа, на который ссылается указатель. Например:
int* ip;
Но в этом случае следует избегать ошибки, которая возможна,
если несколько указателей объявляются в одну строку. Например:
int* p1, p2; /* p1 - указатель, p2 – переменная типа int */
Чтобы в одной инструкции объявить несколько указателей на
один тип, нужно перед каждым указателем ставить символ
*
. На-
пример:
int *p1, *p2; /* p1, p2 - указатели */
Часть I. Язык программирования С
68
При объявлении указателя желательно сразу же его проинициа-
лизировать, что значительно облегчает отладку программы. Если
при объявлении указателя его начальное значение неизвестно, то
указателю нужно присвоить значение символической константы
NULL
, которая равна нулю. Например:
int *p = NULL;
Отметим, что нулевые адреса памяти, как правило, используются
операционной системой и не распределяются программе. Поэто-
му если значение указателя равно
NULL
, то при отладке програм-
мы сразу видно, что этому указателю забыли присвоить нужное
значение. Если же указатель не инициализировать при объявле-
нии, то в нем будет находиться "неопределенное значение".
В этом случае обращение к памяти через такой указатель являет-
ся ошибкой, которую довольно сложно обнаружить.
Часто для числовых типов данных и указателей используют об-
щее название —
скалярные типы данных
.
5.2. Преобразование
типов указателей
В языке программирования C можно преобразовывать типы ука-
зателей. Явное и неявное преобразование типов указателей под-
чиняется следующим правилам:
указатель на любой тип данных может быть приведен к указа-
телю на любой другой тип данных. Если приведение типов
указателей неявное — то компилятор может выдать преду-
преждение;
указатель типа
void*
неявно приводится к указателю на любой
тип данных и наоборот, причем эти операции взаимно обра-
тимы;
указатель может быть преобразован в интегральный тип дан-
ных и наоборот, при этом указатель рассматривается как чис-
ло без знака; если преобразование выполняется неявно, то
компилятор может выдать предупреждение;
Глава 5. Указатели и массивы
69
выравнивание адресов на границы, кратные длине типа дан-
ных, не учитывается, поэтому операция преобразования типов
указателей может дать неопределенный результат;
указатель, значение которого равно
NULL
, преобразовывается в
целое число, значение которого равно 0, и наоборот.
В языке программирования С++ приведение типов указателей
подчиняется следующим правилам:
указатель на любой тип данных может быть приведен к указа-
телю на любой другой тип данных только явно;
указатель типа
void*
можно только явно привести к указателю
на любой тип данных; указатель на любой тип данных неявно
приводится к указателю типа
void*
;
указатель может быть преобразован в интегральный тип и на-
оборот только явно.
Остальные два правила остаются без изменения.
5.3. Операторы определения адреса
и обращения по адресу
Для получения адреса переменной или именованной константы
используется оператор
&
, который называется
оператором опре-
деления адреса
и имеет следующий вид:
&операнд
Здесь
операндом
должно быть L-value, которое удовлетворяет
двум ограничениям:
не указывает на битовое поле;
не имеет спецификатор класса памяти
register
.
Например:
int n;
int *p = &n; /* p указывает на переменную n */
Получить значение переменной или именованной константы, ад-
рес которой хранится в указателе, можно при помощи
оператора
Часть I. Язык программирования С
70
обращения по адресу
или, другими словами,
оператора разыме-
нования
, который имеет следующий вид:
*операнд
Здесь
операндом
должен быть указатель. Возвращает оператор
обращения по адресу L-value. Например:
int n = 2, m;
int *p = &n;
m = *p; /* m = 2 */
*p = 3; /* n = 3 */
Особенно отметим, что работа оператора обращения по адресу не
определена, если указатель содержит ошибочный адрес. В этом
случае может произойти как прерывание программы, так и ус-
пешное завершение операции разыменования, но полученные
данные будут ошибочными. Ошибку, возникшую во втором слу-
чае, особенно трудно обнаружить.
Два последовательных оператора получение адреса и обращение
по адресу аннулируют друг друга независимо от порядка их сле-
дования.
Приоритет операторов определения адреса и обращения по адре-
су совпадает с приоритетом унарных арифметических и побито-
вых операторов. Эти операторы ассоциативны справа налево.
5.4. Указатели на константы
и константные указатели
При работе с указателями следует различать использование мо-
дификатора доступа
const
перед типом переменной, на которую
ссылается указатель, и перед самим указателем. В первом случае
указатель ссылается на типизированную константу, а во вто-
ром — является константным указателем. Например:
const int n = 2;
const int *p = NULL; /* p – указатель на константу */
p = &n; /* правильно, т. к. p – указатель на константу */