Присваивание указателей
Дополнительные сведения о разыменовании указателей
Операции с указателями
Недействительный указатель
Недействительный указатель (invalid pointer) – это ненулевой указатель, содержимое которого не является адресом переменной или функции. Любое использование недействительного указателя (разыменование такого указателя, сравнение его со значением NULL, передача его в качестве параметра в функцию) в стандарте языка Си приводит к неопределенному поведению программы. Рассмотрим возможные причины появления недействительных указателей.
Имеется ряд причин появления недействительных указателей:
· Отсутствие инициализации указателей,
· Ошибки, допущенные при преобразовании целочисленных значений к указательному типу.
· Освобождение динамической памяти.
· Ошибки, допущенные при использовании адресной арифметики.
Здесь ограничимся рассмотрением только первой из перечисленных выше причин. Одна из наиболее распространенных причин появление недействительных указателей связана с отсутствием инициализации указателей. Ниже приводится типичный пример программного кода, в котором используется неинициализированный указатель.
/* Ошибка в программном коде*/
double x;
double* pn;
x = modf(10.2, pn); // Использование неинициализированного
// указателя
/* .............................*/
В языке Си указатели наделены большими возможностями. Об этом свидетельствует размер перечня тех операций, которые можно выполнять с указателями:
· Разыменование,
· Взятие адреса,
· Присваивание,
· Индексирование указателей
· Арифметические действия с указателями,
· Сравнение указателей.
Как отмечалось выше, разыменование – операция позволяющая получить доступ к объекту, на который установлен указатель. Однако эта операция допустима не ко всем указателям. Остановимся на тех категориях указателей, к которым нельзя применять разыменование:
· Недействительный указатель.
· Нетипизированный указатель.
· Нулевой указатель
Приведем примеры недопустимых разыменований.
/* Примеры недопустимых разыменований указателей */
int* p = NULL;
*p = 10; /* Недопустимо разыменовывать нулевой указатель*/
int n = 5;
void* p3 = &n;
*p3 = 10; /* Недопустимо разыменовывать нетипизированный
указатель p3*/
char* p4;
*p4 = ’A’; /* Недопустимо разыменовывать
неинициализированный указатель p4 */
/* . . . . . . . . . . . . . . . . . . . . . . . . . . . */
Заметим, что к категории операторов разыменования относятся еще два оператора:
· Оператор [], который используется при работе с массивами.
· Оператор ->, который используется при работе со структурами.
Одному указателю можно присвоить значение другого указателя. Однако некоторые присваивания, которые допускает язык Си, могут привести к некорректной работе программы. Остановимся на этом вопросе подробнее. Рассмотрим ряд частных случаев. Начнем с присваивания типизированных указателей. Корректность такой операции не вызывает сомнений только в том случае, когда совпадают базовые типы обоих указателей. В противном случае такую операцию следует считать недопустимой. Приведем пример программного кода, в котором будут встречаться оба вида присваиваний.
/* Корректные и некорректные присваивания типизированных
указателей */
int n = 10;
double x = 20.3;
int *p1, p2;
double* p3;
p1 = &n; /* допустимое присваивание */
p2 = p1; /* допустимое присваивание */
p3 = p2; /* недопустимое присваивание, т. к. не
совпадают базовые типы указателей */
/* конец программного кода примера */
Отметим, что, несмотря на семантическую недопустимость присваивания p3 = p1 в приведенном выше примере компилятор языка Си ограничивается выводом предупреждения (Компилятор языка С++ в этой ситуации выводит сообщение об ошибке). Работа языка Си с указателями свидетельствует о его пониженной типизации. Это обстоятельство следует рассматривать как крайне опасным последствиям. Одна из рекомендаций по преодолению этих неприятностей заключается в предварительной компиляции программного кода компилятором языка С++.
Одному указателю можно присвоить значение другого указателя. Однако некоторые присваивания, которые допускает язык Си, могут привести к некорректной работе программы. Остановимся на этом вопросе подробнее. Рассмотрим ряд частных случаев. Начнем с присваивания типизированных указателей. Корректность такой операции не вызывает сомнений только в том случае, когда совпадают базовые типы обоих указателей. В противном случае такую операцию следует считать недопустимой. Приведем пример программного кода, в котором будут встречаться оба вида присваиваний.
/* Корректные и некорректные присваивания типизированных
указателей */
int n = 10;
double x = 20.3;
int *p1, p2;
double* p3;
p1 = &n; /* допустимое присваивание */
p2 = p1; /* допустимое присваивание */
p3 = p2; /* недопустимое присваивание, т. к. не совпадают
базовые типы указателей */
/* конец программного кода примера */
Отметим, что, несмотря на недопустимость присваивания p3 = p1 в приведенном выше примере компилятор языка Си ограничивается выводом предупреждения (Компилятор языка С++ в этой ситуации выводит сообщение об ошибке).