Использование разных типов указателей...109

Каждое выражение, как и переменная, имеет свой тип и значение. Тип выражения &intVar — указатель на переменную типа int, т.е. это выражение имеет тип int*. При сравнении его с объявлением указателя pintVar становится очевидно, что они одинаковы:

    int* pintVar = &intVar ; /*  Обе части этого присвоения имеют тип *int  */

Аналогично pintVar имеет тип int* , a *pintVar — тип int:

    *pintVar = 10 /* Обе части этого присвоения имеют тип int */ 

Тип переменной, на которую указывает pintVar, — int. Это эквивалентно тому, что если houseAddress является адресом дома, то, как ни странно, houseAddress указывает дом. Указатели на переменные других типов объявляются точно так же:

    double doubleVar

    double* pdoubleVar = &doubleVar

    *pdoubleVar = 10.0

_________________

109 стр. Глава 8. Первое знакомство с указателями в С++

В компьютере класса Pentium размер указателя равен четырём байтам, независимо от того, на переменную какого типа он указывает[ 13 ].

Очень важно следить за соответствием типов указателей. Представьте, что может произойти, если компилятор в точности выполнит такой набор команд:

    int n1 ;

    int* pintVar ;

    pintVar = &n1 ;

    *pintVar = 100.0 ;

Последняя строка требует, чтобы по адресу, выделенному под переменную размером в четыре байта, было записано значение, имеющее размер восемь байтов. На самом деле ничего страшного не произойдёт, поскольку в этом случае компилятор приведёт 100.0 к типу int перед тем, как выполнить присвоение.

Привести переменную одного типа к другому явным образом можно так:

    int iVar ;

    double dVar = 10.0 ;

    iVar = ( int )dVar ;

Так же можно привести и указатель одного типа к другому:

    int* piVar ;

    double dVar = 10.0 ;

    double* pdVar ;

    piVar = ( int* )pdVar ;

Трудно предсказать, что может случиться, если сохранить переменные одного типа по адресам, выделенным под переменные другого типа. Сохранение переменных, имеющих большую длину, вероятно, приведёт к уничтожению переменных, расположенных рядом. Такая ситуация наглядно продемонстрирована с помощью программы Layout Error:

      /* LayoutError — демонстрирует результат неаккуратного обращения с указателями */

      #include <cstdio>

      #include <cstdlib>

      #include <iostream>

      using namespace std ;

      int main( int intArgc , char* pszArgs[ ] )

      {

                    /* печать кириллицы, если Вы не установите программки gccrus.exe и g++rus.exe */

            setlocale ( LC_ALL , ".1251" ) ;

        int upper = 0 ;

        int n = 0 ;

        int lower = 0 ;

          /* Вывод адресов каждой из переменных. Обратите внимание на расположение переменных в памяти компьютера */

        cout << "&upper = 0x" << &upper<< " " ;

        cout << "&n      = 0x" << &n  << " " ;

        cout << "&lower  = 0x" << &lower << " " ;

          /* Выводим значения объявленных переменных */

          cout << "upper = " << upper << " " ;

          cout << "n = " << n << " " ;

          cout << "lower = " << lower << " " ;

          /* Сохраняем значение типа double в памяти, выделенной для int */

          cout << " Сохранение double в int " ;

        cout << " Сохранение 13.0 по адресу &n " ; 

          double* pD = ( double* )&n ;

          *pD = 13.0 ;

          /* Показываем результаты */

          cout << "upper = " << upper << " " ;

          cout << "n = " << n << " " ;

          cout << "lower = " << lower << " " ;

          /* Пауза для того, чтобы посмотреть на результат работы программы */

          system ( "PAUSE" ) ; return 0 ;

      }

__________

13Размер указателя зависит не только от типа процессора, но и от операционной системы, используемого компилятора и так называемой модели памяти создаваемой программы. — Прим. ред.

_________________

110 стр. Часть 2. Становимся функциональными программистами

В первых трёх строках функции main( ) происходит объявление трёх переменных типа int. Допустим, что в памяти эти переменные находятся друг за другом.

Следующие три строки выводят значения этих переменных на экран. Не удивительно, что все три оказываются равными нулю. После этого происходит присвоение *pD = 13.0 ; , в результате которого число, имеющее тип double, записывается в переменную n, имеющую тип int. Затем все три переменные снова выводятся на экран.

После записи действительного числа в целочисленную переменную n переменная upper оказалась "забитой" каким-то мусором, что видно из результата работы программы:

 

    upper = 0

    n = 0

    lower = 0

    Сохранение double в int

    upper = 1076494336

    n = 0

    lower = 0

    Press any key to continue...

На языке домов и адресов эта программа будет выглядеть так:

    house* houseAddress = &"123 Main Street" ;

    hotel* hotelAddress ;

    hotelAddress = ( hotel* )houseAddress ;

    *hotelAddress = TheRitz ;

Указатель houseAddress инициализирован как указатель на мой дом. Переменная hotelAddress содержит адрес отеля. После этого вместо адреса моего дома записывается адрес отеля. Затем отель "Ритц" устанавливается по адресу моего дома. Однако поскольку "Ритц" куда больше моего дома, не удивительно, что он уничтожит не только мой дом, но и дома моих соседей ( хоть что-то приятное в результате ошибки! ).

Типизация указателей предохраняет программиста от неприятностей, связанных с сохранением данных большего размера в меньшем объёме памяти. Присвоение *pintVar = 100.0 не вызывает никаких проблем, поскольку С++ известно, что pintVar указывает на целочисленную переменную и приводит 100.0 перед присвоением к тому же типу.