Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

Лабораторная работа

Работа добавлена на сайт samzan.net:



Рубанчик В.Б.

Лабораторная работа "Передача массивов в функции"

6/6


Р
Л

ЛАБОРАТОРНАЯ РАБОТА

Тема:               Передача массивов в функции

Цель работы: Изучить особенности использования массивов как аргументов функций

Имена массивов и указатели

В Си разница между именем одномерного массива и указателем на тип элементов этого массива проявляется в двух случаях:

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.

Объяснить результаты работы программы.

Другой способ описания типа для аргумента-массива

Чтобы повысить самодокументируемость программ (сделать очевидным, что по смыслу программы аргумент функции принимает массив) в Си предлагается второй способ описания аргументов-массивов:

void f(int a[]);    (1)

Однако нужно понимать, что на самом деле это только более удобный для восприятия синтаксис и различия  с  void f(int *a); чисто внешние. Компилятор, встретив определение аргумента в виде int a[],  заменит его на  int *a.

По-прежнему при передаче в функцию будет утерян размер фактического массива.

Замечание

Не будет синтаксической ошибкой, если в квадратных скобках указать размер массива — void f(int a[12]). Однако с практической точки зрения для одномерного массива это бесполезно, так как размер учитываться никак не будет.

ЗАДАНИЕ 2 (передача одномерного массива)

1. В функции main определяется и инициализируется  массив из нескольких чисел типа double.

Написать функцию total, которой передается  массив чисел типа  double, Функция суммирует элементы этого массива и возвращает полученную сумму.

Внутри функции размер массива вычислить нельзя. Поэтому при вызове функции total передается не только массив, но и количество элементов  в нем.

Чтобы сделать программу более универсальной и не привязывать ее к конкретному размеру, количество элементов массива вычисляется в функции main: с помощью операции  sizeof следующим образом: получаем размер памяти, занятой массивом,  и делим результат на размер типа double.  Затем найденное значение подставляется  в вызов функции total.

Вывести  на экран:

а) вычисленное  количество элементов в массиве,

б) значение полученной суммы чисел.

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

Передача в функцию двумерного массива

Почти все рассуждения, которые относятся к передаче одномерного массива в функцию, остаются справедливыми и для двумерных массивов. Исключение составляет тип данных формального параметра, соответствующего массиву.

Замечание

Для двумерных массивов (т.е. матриц) важнейшей характеристикой является количество элементов в строке (или столбцов матрицы). Действительно, 20 чисел можно представить в виде двумерного массива многими способами: 20*1, 2*10, 4*5, 10*2 и т.д. Так как элементы массивов перебираются по строкам (например, при инициализации), то  задание количества элементов в строке однозначно определит структуру массива.

В Си имя массива — это указатель на тип элемента массива. Двумерный массив — это одномерный массив строк матрицы (тоже одномерных массивов). Поэтому имя двумерного массива — это указатель на строку матрицы, т.е. на массив из конкретного числа элементов.

Пример

В функцию f нужно передать единственный аргумент — двумерный массив arr2 чисел типа int. Размеры массива:  6 строк и 8 столбцов.

Тогда прототип функции должен будет выглядеть следующим образом (с точностью до имени формального параметра):

void f(int (*a)[8]);           (2)

Замечания

1. Если при передаче одномерного массива конкретные размеры массива указывать было не нужно, то для двумерного массива обязательно должно быть указано количество столбцов (второй размер, в примере это 8).

Конструкция  (*a)[8] читается, как "указатель на массив из 8 элементов" (запись *a[8] означает "массив из 8 указателей", что не соответствует условию примера).

2. Двумерный массив — это одномерный массив строк матрицы.  Поэтому в функции информация о количестве его элементов (строк переданного ей двумерного массива) теряется.

Для описания аргументов, принимающих двумерные массивы, также имеется  вторая синтаксическая форма:

void f(int a[][8]);

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

Если указать первый размер, то ошибки компиляции не будет, но этот размер никак использоваться не будет.

ЗАДАНИЕ 3 (передача в функцию  двумерного массива)

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

1. Функция main.

1.1. В функции main определен двумерный числовой массив arr2[][3] со значениями типа int. Массив инициализирован так, чтобы в нем было 4 полных строки (т.е. размеры 4*3).

1.2. С помощью sizeof определяется размер объекта arr2. Используя этот размер и известное количество столбцов (их 3), подсчитать количество строк в массиве. Вывести на экран оба результата.

1.3. Выполнить вызов функции total2, передав ей

а) массив arr2,  

б) количество строк в массиве.

2. Функция total2.

В функции total2 выполняются следующие действия.

2.1. С помощью sizeof определяется размер объекта, соответствующего формальному параметру-массиву, и этот размер выводится на экран.

Сравнить эту величину с той, что была получена в первом пункте задания, и объяснить результаты.

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

Замечание

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

Теперь количество элементов в строке можно найти, если объем памяти, занимаемый строкой, поделить на размер одного элемента (sizeof(*a2)/sizeof(int)).

2.3 Проверить работу программы.

ЗАДАНИЕ 4 (универсальный вариант  программы)

Разрабатывается другой вариант программы для задачи предыдущего задания.  Для преобразований программы, сохранить файл задания 3 под другим именем.

1. Чтобы сделать функцию total2 более универсальной, критический для массива размер (число колонок) везде, где он встречается (в определении массива arr2, в определениях функции и прототипа, граничное значение в операторе цикла), задать с помощью именованной константы COLUMNS.

Определение константы COLUMNS выполнить в начале файла с помощью директивы

#define COLUMNS  3

Проверить работу программы.

Замечание

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

Именованная константа, задающая этот размер, доступна в любой части программы, в том числе и в функции total2.

4. Чтобы убедиться, что внутри функции total2 обрабатываются непосредственно элементы массива arr2, добавить в total2  (перед оператором возврата) обнуления первой строки массива.

Соответственно, в функции main добавить после оператора вывода на экран подсчитанной суммы  печать элементов первой строки массива arr2.

Изменить  значение COLUMNS  на 4, затем на 6. Убедиться, что сумма подсчитывается правильно, а количество выведенных обнуленных элементов меняется соответственно размеру.

Контрольные вопросы

1. Почему при передаче массива в функцию копируется не "значение" объекта, т.е. все элементы, а только указатель на начало массива?

2. Когда в функцию передается простая переменная, то в вызванной функции изменения значения соответствующего аргумента на фактической переменной никак не сказываются. Но изменение в функции элемента аргумента-массива является изменением элемента  фактического массива. Объяснить обе ситуации.

3. Есть ли принципиальное различие между двумя способами описания аргументов, через которые передаются массивы? Если нет, то объяснить, почему?

4. Если в функцию передается двумерный массив, какие данные необходимо передать в эту функцию, чтобы можно было вести обработку массива?

5. Что подразумевается, когда говорят, что при передаче массива в функцию "теряется" один его размер?




1. Дагестанская государственная медицинская академия Д
2. Слезы и смех Чарльза Диккенса
3. Развитие медицины и фармации в эпоху ятрохимии.html
4. ОБЩИЕ ТРЕБОВАНИЯ [3] 2
5. Пояснительная записка
6. Дискретная случайная величина Х задана законом распределения- х
7. клеёнка для маркировки бикса
8. Курсовая работа- Парки та сади Чернігівщини
9. ель прийшов скрикнуло дівча
10.  С Новым годом Дорогие друзья Поздравляем Вас с наступающим Новым годом Желаем в следующ
11. Учение о душе человека
12. Теоретические модели посттравматического стрессового расстройства
13. РЕФЕРАТ дисертації на здобуття наукового ступеня кандидата наук з фізичного виховання і спорту1
14. 024ПЗ Разраб
15.  2013 г
16. документ предоставляющий право на какоелибо обслуживание
17. Исповедь I Я был крещен и воспитан в православной христианской вере
18. Коммерческие. Целью стоит увеличение продаж с помощью рекламы.
19. Об общественном контроле за обеспечением прав человека в местах принудительного содержания и о содействии л
20. Оставляю за вами право редактировать свои появления дополнить удалить но так чтобы всё привело к концовке