- •Часть 2
- •18 Сентября 2012 г., протокол № 1
- •Предисловие
- •Глава 6 простые типы данных § 1. Целый тип
- •1.1. Битовые операции
- •Использование битовых операций
- •1.3. Упаковка и распаковка информации
- •§ 2. Логический тип
- •§ 3. Символьный тип
- •§ 4. Вещественный тип
- •§ 5. Преобразование типов
- •Преобразование типов в выражениях
- •Преобразование типов при присваивании
- •Г л а в а 7 введение в указатели
- •§ 1. Понятие указателя. Операции разыменования и разадресации
- •§ 2. Инициализация и присваивание указателей
- •§ 3. Распределение динамической памяти
- •Операция new
- •Операция delete
- •§ 4. Параметры-указатели. Функция ввода scanf
- •Упражнения, тесты
- •Г л а в а 8 одномерные массивы, указатели и функции
- •§ 1. Связь указателей и одномерных массивов. Передача массива в качестве параметра функции
- •§ 2. Сортировка одномерных массивов
- •§ 3. Сортировка массива по параметру числа
- •§ 4. Сортировка массива выбором.
- •§ 5. Сортировка массива вставками
- •§ 6. Динамические одномерные массивы
- •6.1. Порядок работы с динамическим массивом
- •6.2. Работа с динамическим массивом в классе. Деструктор
- •§ 7. Введение в строки
- •Глава 9 мАтрицы
- •§ 1. Объявление, способы определения матриц
- •§ 2. Вывод матриц
- •§ 3. Типы алгоритмов
- •3.1. Построчная обработка
- •Обработка матрицы по столбцам
- •3.3. Обработка всей матрицы
- •3.4. Обработка части матрицы
- •Преобразование матрицы
- •Построение матриц
- •§ 4. Матрицы, указатели и функции
- •Упражнения и тесты
- •Обработка матрицы по столбцам.
- •Список реКоМендуемой литературы
- •Сборники задач по программированию
- •Оглавление
- •Методы программирования:
- •Лекции, примеры, тесты
- •Пособие для студентов механико-математического факультета
- •В двух частях
- •Часть 2
§ 2. Инициализация и присваивание указателей
Переменную-указатель можно также проинициализировать, используя ранее определённую переменную-указатель. Корректным будет следующее объявление: int* q=p; что равносильно int *q; q=p; (а не *q=p;). После этого q и p будут содержать один и тот же адрес, адрес ячейки t. Поэтому *p и *q —это одно и то же целочисленное значение t. Заметим, что присваивания *q=p; *p=q; q=*p; p=*q; p=t; q=t; некорректны. Объяснить, почему.
Нельзя переменную-указатель проинициализировать константой или определённым ранее значением переменной.
a) int *p=1000; /* Ошибка!!! */
b) int *p; p=1000; /* Ошибка!!! */
c) int *p, k; cin>>k; p=k; /* Ошибка!!!. */
Это связано с тем, что система сама должна “найти” свободную ячейку и её адрес присвоить нашей переменной. Наша “помощь” будет отвергнута. Переменную-указатель нельзя также вводить. Поэтому cin>>p; недопустимо.
Не будет ошибкой, если во время объявления указателя мы его не проинициализируем. Но до его первого использования надо не забыть это сделать, например, с помощью присваивания: float f=1.1, *p1, *p2; … p1=&f; … p2=p1;
cout<<(*p1)<<” “<<(*p2); В результате будет выведено дважды одно и то же число 1.1, если, конечно, значение f, или, что то же самое, *p1 или *p2 не меняли.
Присваивание указателей допустимо, если оба указателя содержат адрес ячеек с данными одного и того же типа. Поэтому следующий фрагмент
double x=12.34, y; int *w; w=&x; y=*w; printf (“%f”, y); // Ошибка!!!
не приведёт к желаемому результату 12.34. Во время компиляции w=&x; появится сообщение об ошибке, связанное с несоответствием типов. Но если int* w; заменить на double * w, то программа выведет 12.34.
§ 3. Распределение динамической памяти
Операция new
Операция new — один из способов определения значения переменной указателя. Ее синтаксис такой: указатель = new имя_типа; Например, если объявлен указатель float *p; то операция p=new float; выполняет следующее:
1. Выделяет и делает доступным свободный участок динамической памяти, размер которого соответствует типу данных, определяемому именем типа (в примере 4 байта для размещения вещественного числа).
2. Кроме этого, в случае успешного выполнения операция new возвращает адрес начала выделенного участка. В примере значение этого адреса присваивается переменной-указателю (переменной p).
Но значение выделенной ячейки, которое в программе обозначается *p, после выполнения этой операции ещё не определено. Поэтому cout<<(*p) выведет случайное число. Для задания значения *p надо выполнить, например, ввод cin >> (*p); или использовать другой способ (присваивание, получение с помощью функции и т. п.). Обратим внимание, что речь идёт об определении *p, а не p. Поэтому напомним, что cin>>p; или p=1.1; ошибочны.
Если участок нужных размеров не может быть “найден”, то операция new возвращает нулевое (точнее, неопределённое) значение адреса. Оно обозначается в программе константой NULL, которая записывается обязательно большими буквами. Тогда в программе после p=new float; желательно записать:
if (p==NULL) // или if (!p)
{ cout<<”\nError”; getch(); exit(0);}.
Как нулевое числовое значение, так и значение NULL для указателей равносильно false в операциях сравнения.
Стандартная функция exit(0) прекращает выполнение всего проекта. Она более “сильная” по сравнению с оператором return, так как осущетвляет выход не только из функции, где записана, а останавливает работу всего проекта. Заметим, что самым “слабым” в этой группе является оператор break, который прерывает всего лишь цикл или оператор выбора switch, продолжая выполнение функции, если цикл или switch не были последними в ней.
С помощью рассматриваемой операции new можно проинициализировать не только значение указателя, но и задать значение выделенной ячейки памяти. В таком случае операция применяется так: указатель = new имя_типа(значение);. Например, p=new float(3.1); определяет как значение указателя p, так и значение *p. Это равносильно p=new float; *p=3.1; Заметим, что значение записывается в круглых, а не в квадратных скобках. Квадратные скобки будем позже использовать для создания динамического массива. Но динамический массив так инициализировать нельзя! (см. следующую главу). Доступ к переменной, адрес которой находится в p, выполняется операцией “*” (разыменование). Поэтому cout<<p<<” “ <<(*p); выведет шестнадцатеричный адрес и число 3.1.
