Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lekcii_1_10 / Лекция 06.docx
Скачиваний:
71
Добавлен:
21.05.2015
Размер:
475.25 Кб
Скачать

О важности базового типа указателя

На примере предыдущей программы была показана возможность присвоения переменной value значения переменной balance посредством операции непрямого доступа, т.е. с использованием указателя. Возможно, при этом у вас промелькнул вопрос: "Как С++-компилятор узнает, сколько необходимо скопировать байтов в переменную value из области памяти, адресуемой указателем balptr?". Сформулируем тот же вопрос в более общем виде: как С++-компилятор передает надлежащее количество байтов при выполнении операции присваивания с использованием указателя? Ответ звучит так. Тип данных, адресуемый указателем, определяется базовым типом указателя. В данном случае, поскольку balptr представляет собой указатель на целочисленный тип, С++-компилятор скопирует в переменную value из области памяти, адресуемой указателем balptr, четыре байт информации (что справедливо для 32-разрядной среды), но если бы мы имели дело с double-указателем, то в аналогичной ситуации скопировалось бы восемь байт.

Переменные-указатели должны всегда указывать на соответствующий тип данных. Например, при объявлении указателя типа int компилятор "предполагает", что все значения, на которые ссылается этот указатель, имеют тип int. С++-компилятор попросту не позволит выполнить операцию присваивания с участием указателей (с обеих сторон от оператора присваивания), если типы этих указателей несовместимы (по сути не одинаковы).

Например, следующий фрагмент кода некорректен.

int *р;

double f;

// ...

р = &f; // ОШИБКА!

Некорректность этого фрагмента состоит в недопустимости присваивания double-указателя int-указателю. Выражение &f генерирует указатель на double-значение, а р — указатель на целочисленный тип int. Эти два типа несовместимы, поэтому компилятор отметит эту инструкцию как ошибочную и не скомпилирует программу.

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

int *р;

double f;

// ...

р = (int *) &f; // Теперь формально все ОК!

Операция приведения к типу (int *) вызовет преобразование double- к int-указателю. Все же использование операции приведения в таких целях несколько сомнительно, поскольку именно базовый тип указателя определяет, как компилятор будет обращаться с данными, на которые он ссылается. В данном случае, несмотря на то, что p (после выполнения последней инструкции) в действительности указывает на значение с плавающей точкой, компилятор по-прежнему "считает", что он указывает на целочисленное значение (поскольку р по определению — int-указатель).

Чтобы лучше понять, почему использование операции приведения типов при присваивании одного указателя другому не всегда приемлемо, рассмотрим следующую программу.

// Эта программа не будет выполняться правильно.

#include <iostream>

using namespace std;

int main()

{

 double x, у;

 int *p;

 x = 123.23;

 p = (int *) &x; // Используем операцию приведения типов для присваивания double-указателя int-указателю.

 у = *р; // Что происходит при выполнении этой инструкции?

 cout << у; // Что выведет эта инструкция?

 return 0;

}

Как видите, в этой программе переменной p (точнее, указателю на целочисленное значение) присваивается адрес переменной х (которая имеет тип double). Следовательно, когда переменной y присваивается значение, адресуемое указателем р, переменная y получает только четыре байт данных (а не все восемь, требуемые для double-значения), поскольку р— указатель на целочисленный тип int. Таким образом, при выполнении cout-инструкции на экран будет выведено не число 123.23, а, как говорят программисты, "мусор". (Выполните программу и убедитесь в этом сами.)

Соседние файлы в папке lekcii_1_10