Рубанчик В.Б. |
Лабораторная работа "Передача массивов в функции" |
|
Лабораторная работа
Тема: Передача массивов в функции
Цель работы: Изучить особенности использования массивов как аргументов функций
Имена массивов и указатели
В Си разница между именем одномерного массива и указателем на тип элементов этого массива проявляется в двух случаях:
1. Использование имени массива в роли указателя.
Имена массивов трактуются компилятором как адреса, а именованные адреса в Си называются указателями.
Обычно под указателем понимают переменную, т.е. объект, который при выполнении программы может изменить свое значения. Для изменения значения переменная должна быть помещена в левую часть оператора присваивания (использоваться как l-value).
Но имена массивов — это особый тип указателей. Они обозначают адрес, который меняться не может (указатель-константа). Поэтому имена массивов (без индексов) могут появляться только в правых частях выражений (использоваться только, как r-value)
Пример 1.
В программе определены одномерный массив и указатель, который инициализируется адресом массива.
int arr[12], *pa=arr;
arr++ ; /* Ошибка! Объяснить, почему? */
pa++; /* Правильно */
2. Использование имени массива для вычисления размер объекта.
Результатом применения операции sizeof к имени массива будет размер отведенной массиву памяти (в байтах).
Если применить операцию к однотипному с массивом указателю (pa), то будет вычислен размер указателя, т.е. объем памяти, используемый в программе для хранения адреса.
Передача одномерного массива в функцию
Под передачей массива в функцию будем понимать использование имени массива в качестве фактического параметра функции, например, f(arr); .
В Си аргументы функций передаются значением, т.е. в формальный параметр копируется значение фактического. Когда речь идет о простых переменных, то это проблем не вызывает.
Но, когда нужно передать в функцию массив, то копирование всего массива будет означать громоздкую операцию дублирования большого объема данных.
Поэтому авторами языка Си было решено, что при передаче массива в функцию вместо самого массива копируется только указатель на его начало. Это имеет два следствия.
а) Во-первых, формальный параметр функции, соответствующий аргументу-массиву, объявляется как указатель на тип элемента массива.
а) Во-вторых, так как в функцию передается указатель на реальный массив, то изменение элементов массива внутри функции будет означать изменение элементов фактического массива. Это принципиально отличает передачу массива от передачи простой переменной.
При передаче массивов в функции требуется учитывать следующие особенности.
а) Тип формального параметра.
Компилятор следит за тем, чтобы типы фактического и формального параметров совпадали. В примере 1 идентификатор массива arr имеет тип указателя на элемент массива (т.е. на int). Поэтому в описании функции или ее прототипа соответствующий этому массиву аргумент также должен иметь тип того же указателя:
void f(int *a){…} (1)
Здесь a — формальный параметр, который внутри функции может использоваться, как имя одномерного массива.
Вызов функции с передачей массива будет выглядеть так: f(arr);
Однако фактический параметр функции f реально не обязательно должен быть именем массива. В приведенном примере требуется только, чтобы фактический параметр имел тип int* (указателя на int).
В примере 1, наряду с массивом, был определен указатель pa (синоним имени массива arr), имеющий необходимый тип. Поэтому вызов f(pa); будет равноценным f(arr);.
б) Потеря информации о размере.
Когда массив передается в функцию, в этом участвует только адрес первого элемента массива. Это означает, что размер массива никак не учитывается, и в функции он неизвестен. Поэтому обычно функции добавляется еще один дополнительный аргумент, через который передается размер массива.
Когда вместо массива передается обычный указатель (pa), разговор о размере массива в обычном смысле вообще не теряет смысл. Например, может быть передан указатель не на первый, а на любой другой элемент массива.
Эти рассуждения можно обобщить на случай массивов любой размерности: при передаче массива в функцию информация о его первом (левом) размере теряется.
ЗАДАНИЕ 1 (анализ размеров объектов)
1. Массив arr типа int и указатель pa определены как в примере 1.
Написать программу, в которой с помощью операции sizeof вычисляются и выводятся на экран размеры объектов arr и pa с поясняющим текстом Massiv и Ukazatel.
Объяснить результаты работы программы.
2. В программу добавить функцию с прототипом void size_of_arg(int* a), в которой вычисляется и выводится на экран размер переменной a.
В main предусмотреть вызов функции size_of_arg два раза. Первый раз передать ей имя массива, а второй раз — указатель pa.
На экран перед результатами должен быть выведен поясняющий текст, соответственно, Peredan massiv и Peredan ukazatel.
Объяснить результаты работы программы.