
- •Глава III. Массивы и указатели
- •9. Массивы
- •9.1. Общие принципы организации массивов
- •9.2. Объявление и инициализация массивов
- •9.3. Заполнение массива данными и вывод элементов массива
- •1. Заполнение массива данными
- •2. Вывод элементов массива
- •9.4. Типовые задачи на обработку одномерных массивов
- •1. Поиск максимального (минимального) значения в массиве и его индекса
- •2. Подсчет суммы значений элементов массива, удовлетворяющих заданному условию
- •3. Подсчет элементов массива, удовлетворяющих заданному условию
- •4. Поиск значений
- •5. Формирование массива по заданному условию
- •6. Перестановка элементов массива в обратном порядке
- •7. Циклический сдвиг элементов массива
- •8. Сортировка элементов массива в порядке возрастания (убывания)
- •9.5. Типовые задачи на обработку двумерных массивов
- •10. Указатели
- •10.1. Назначение указателей
- •10.2. Объявление и инициализация указателей
- •10.3. Операции над указателями
- •10.4. Выражения и арифметические действия с указателями
- •10.5. Связь указателей с массивами
- •10.6. Операторы new и delete. Константа null
- •10.7. Динамические массивы
- •10.8. Массивы указателей
- •Содержание
10.3. Операции над указателями
С указателями связаны два специальных оператора: & и *. Обе эти операции унарные, т.е. имеют один операнд, перед которым они ставятся. Операция & соответствует действию “взять адрес”. Операция * соответствует словам “значение, расположенное по указанному адресу”. Например:
int y=5;
int *py;
py=&y;
Здесь оператор py=&y; присваивает адрес переменной у указателю py. Говорят, что переменная py указывает на y. Оператор * обычно называют оператором косвенной адресации, или операцией разыменования, возвращающей значение объекта, на который указывает ее операнд (т.е. указатель). Например, оператор printf("%d\n",*py); выводит на экран значение переменной у, а именно 5. Использование * указанным способом позволяет обеспечить доступ к величине, адрес которой хранится в указателе.
При объявлении переменной символ * служит признаком указателя, а при реализации * служит знаком использования данных по адресу, если стоит перед указателем.
Пример. Использование операций & и *.
#include <stdio.h>
void main()
{
int a; // a – целое число
int *pa; // pa – указатель на объект типа int
a=7;
pa=&a; // pa устанавливаем равным адресу переменной а
printf("Адрес a: %d Значение pa: %d\n", &a, pa);
printf("Значение a: %d Значение *pa: %d\n", a, *pa);
}
Результаты работы программы:
Адрес a: 1245052 Значение pa: 1245052
Значение a: 7 Значение *pa: 7
Программа демонстрирует операцию с указателями. В программе показано, что адрес переменной а и значение указателя ра идентичны, а операция разыменовывания указателя *pa выводит на экран значение переменной а. Операции & и * взаимно дополняют друг друга.
Пример. Использование операций & и *.
int х=1, у=2;
int *ip;
ip=&x; // ip указывает на x
y=*ip; // y теперь равен 1
*ip=0; // x теперь равен 0
10.4. Выражения и арифметические действия с указателями
Указатели могут использоваться как операнды в арифметических выражениях, выражениях присваивания и выражениях сравнения. Число арифметических операций с указателями ограничено. Указатели можно увеличивать (++), уменьшать (--), складывать с указателем целые числа (+ или +=), вычитать из него целые числа (- или -=) или вычитать один указатель из другого. Арифметические действия над указателями имеют свои особенности. Рассмотрим это на простейшем примере.
#include <stdio.h>
void main()
{
short int x; // x – целое число
short int *p, *p1; // указывают на целые числа
p=&x; // указателю присваивается адрес целого числа х
p1=p+3;
printf("Начальное значение р: %d\n", p);
printf("Конечное значение ++р: %d\n", ++p);
printf("Конечное значение --р: %d\n", --p);
printf("Конечное значение р1: %d\n", p1);
}
Результаты работы программы:
Начальное значение р: 1245052
Конечное значение ++р: 1245054
Конечное значение --р: 1245052
Конечное значение р1: 1245058
По результатам выполнения этой программы мы видим, что при операции ++р значение указателя р увеличивается не на 1, а на 2. И это правильно, так как новое значение указателя должно указывать не на следующий адрес, а на адрес следующего короткого целого. А короткое целое, как известно, занимает два байта памяти. Если бы базовый тип указателя был не short int, a double, то были бы напечатаны адреса, отличающиеся на 8, именно столько байт памяти занимает переменная типа double, т.е. при каждой операции ++р значение указателя будет увеличиваться на количество байт, занимаемых переменной базового типа указателя, а при операции --р соответственно уменьшаться. К указателям можно прибавлять или вычитать некоторое целое. В данной программе указатель р1 представляет собой сумму значений указателя р и целого числа 3. Результат равен 1245058, т.е. увеличился на 6 по сравнению с исходным значением указателя р. Можно так же вычитать один указатель из другого. Так, если р и р1 – указатели на элементы одного и того же массива, то операция р–р1 дает такой же результат, как и вычитание соответствующих индексов массива. Указатели можно сравнивать, при этом применимы все 6 операций:
<, >, <=, >=, = , == , !=.
Сравнение p<g означает, что адрес, находящийся в р, меньше адреса, находящегося в g.