МУ_ЛР5_ОП (Методические указания к лабораторным работам), страница 2
Описание файла
Файл "МУ_ЛР5_ОП" внутри архива находится в папке "Методические указания к лабораторным работам". Документ из архива "Методические указания к лабораторным работам", который расположен в категории "". Всё это находится в предмете "программирование на основе классов и шаблонов" из 2 семестр, которые можно найти в файловом архиве МГТУ им. Н.Э.Баумана. Не смотря на прямую связь этого архива с МГТУ им. Н.Э.Баумана, его также можно найти и в других разделах. Архив можно найти в разделе "книги и методические указания", в предмете "программирование на основе классов и шаблонов" в общих файлах.
Онлайн просмотр документа "МУ_ЛР5_ОП"
Текст 2 страницы из документа "МУ_ЛР5_ОП"
<Описание функции> :=[<Спецификация типа возврата функции>] <Название функции> ([<Список Формальных параметров>]) {<тело функции>};
Спецификация типа возврата функции – это любой допустимый тип в программе: стандартный (int, float и т.д. ), системный (системные структуры и typedef переменные) и пользовательский (пользовательские структуры в СИ и классы в С++). Допускаются типы с указателями и ссылками. В отдельных случаях можно отказаться от задания типа возврата, тогда используется спецификатор – void. В этом случае функцию нельзя использовать в выражениях. Если спецификатор возврата отсутствует, то подразумевается возврат типа int.
Тело функции – это любой составной оператор, заключенный в фигурные скобки. Завершение выполнения функции выполняется двумя вариантами:
-
При достижении последней в теле функции закрывающей фигурной скобки (“}”).
-
Выполнении в программе специального оператора return.
Оператор return может быть задан в двух видах:
return; // Когда тип возврата viod
return < Выражение, тип которого совпадает с типом возврата функции>;
Список формальных параметров функции – это перечень описаний переменных со специальными именами, которые используются только в этой функции. Разделителем между описаниями является запятая. Имена формальных параметров должны быть уникальными в теле функции и хорошо подобраны по смыслу. Число формальных параметров не ограничивается, но не должно быть большим, для наглядности. Пример описания функции с формальными параметрами:
int MaxMas ( int * iMas , int Razm, int * Max)
{
int TempMax;
TempMax = iMas[0];
for ( int i = 1; i < Razm; i++)
if ( TempMax < iMas[i])
TempMax = iMas[i];
*Max = TempMax;
return *Max;
};
Функция поиска максимального значения в массиве, заданном указателем (iMas), размерностью (Razm) , тип возврата int, возвращаемое максимальное значение указатель (Max).
8 Прототипы функций
Если функция должна быть вызвана в программе одного модуля (например, см. выше first.cpp), а ее описание дано а другом исходном модуле (см. second.cpp), необходимо задать прототип функции – краткое описание заголовка функции без ее тела. Даже при описании функции в одном исходном модуле требуется прототип, если вызов функции планируется до ее описания (оно может располагаться ниже в исходном модуле). Прототип позволяет проконтролировать правильность задания параметров при вызове функции. Прототип задается так:
<Прототип функции> :=[<Спецификация типа возврата функции>] <Название функции> ([<Список типов параметров [с тегами]>]);
Тело при задании прототипа функции отсутствует, вместо списка параметров задается список типов, в которых можно условно указать любые имена (они иногда называются тегами). Эти имена являются своего рода подсказками и не обязательно должны совпадать с именами формальных параметров, задаваемых в описании функции. Прототипы функции, приведенной выше, могут быть заданы так (все варианты правильные):
int MaxMas ( int * iMas , int Razm, int * Max); // Вариант 1
int MaxMas ( int *, int, int *); // Вариант 2
int MaxMas ( int * piMas , int iRazm, int * piMax); // Вариант 3
int MaxMas ( int * piMas , int iRazm = 10, int * piMax); // Вариант 4, (10) 2-й параметр по-умолчанию
Отметим на будущее, что прототип определяет так называемую сигнатуру описания функции. Если сигнатуры функций различны, например, отличается число параметров (или их типы), то функции в СИ++ могут иметь одинаковые имена. Такая технология программирования называется перегрузкой функций.
9 Вызовы функций и возврат значений функции
Вызов функции это передача управления с возвратом в тело заданной функцией и настройкой на те параметры, которые указаны при этом вызове. Эти параметры также называются фактическими. В качестве параметров , в зависимости от их типа, могут быть заданы выражения. Если в функции параметр может быть изменен, то он должен задаваться указателем. В этом случае значением выражения тоже должен быть указатель.
Формально вызов функции можно записать так:
<Вызов функции> <Название функции> ([<Список выражений для каждого типа параметров функции>]);
Или (чисто терминологически) можно записать так:
<Вызов функции> <Название функции> ([<Список фактических параметров>]);
Пример вызова функции Summa:
int Sum;
int a = 5 , b = 5;
Sum = Summa(2,3); // Вызов с константами
Sum = Summa(a, b); // Вызов с переменными
Sum = Summa(a, a + b); // Вызов с выражением
Sum = Summa(Summa(a,b) ,Summa(2,4)); // Вызов с вызовом функции
Пример вызова функции MaxMas:
// Описание массивов
int iMas[5] = {1,2,3,4,5}; // 0 - 4
int iMas1[] = {1,2,3,1,1,1,1,1}; // 0 - ?
int MaxM , c;
// Вызов Функции для массивов
c = MaxMas (iMas , sizeof(iMas)/sizeof(int) ,&MaxM);
printf ("Максимум в массиве iMas = %d \n " , c );
c = MaxMas (iMas1 , sizeof(iMas1)/sizeof(int) ,&MaxM);
printf ("Максимум в массиве = %d \n" , c );
//
Передача параметров в СИ в функцию выполняется по значению, а это означает, что в явном виде изменить параметр в функции нельзя. Например, после вызова функции (из исходного модуля проекта - second.cpp):
// Попытка возврата суммы через параметр sum
int Summ2 (int a , int b, int sum)
{
sum = (a + b);
return sum; // возвращаемое значение функции
};
…
В главной программе (first.cpp):
…
int Summ2 (int a , int b, int sum);
…
//
int SUM = 10;
Sum = Summa2(2, 3 , SUM ); // Вызов с константами
// Значение SUM = 10 , а Sum = 5 ;
Для обеспечения правильного возврата через параметр в функцию нужно передать указатель (из second.cpp):
int Summ3 (int a , int b, int * psum) // Параметр указатель
{
*psum = (a + b);
return *psum; // возвращаемое значение функции
};
//
…
В главной программе (first.cpp):
…
int Summ3 (int a , int b, int * psum);
//…
Sum= Summ3 (2 , 3, &SUM); // Вызов с указателем
// Sum= 5 и SUM = 5
В дополнение к двум рассмотренным вариантом возврата значений из функций ( через тип функции и через указатель), принципиально, можно возвратить значение и через глобальную переменную (у нас переменная GSum), хотя этот стиль программирования очень плохой, с позиций надежности программы. Все равно покажем пример (из second.cpp):
// Возврат суммы через глобальный параметр GSum
extern int GSum;
int Summ2 (int a , int b, int sum)
{
sum = (a + b);
GSum = sum; // возвращаемое значение через глобальную переменную
return sum; // возвращаемое значение функции
};
…
В главной программе (first.cpp):
…
int Summ2 (int a , int b, int sum);
int GSum;
…
Sum = Summa2(2, 3 , SUM ); // Вызов с константами
// Значение SUM = 10 , а Sum = 5 GSum = 5
10 Переменные в функциях
В пределах составного оператора (“тела функции”) доступны для операций, выполняемых внутри функции следующие данные:
-
Формальные параметры, передаваемые при вызове функции.
-
Локальные переменные, описанные в теле функции.
-
Константы разного типа.
-
Глобальные параметры данного исходного модуля (где описана функция) и тех, которые получены с помощью спецификатора extern.
Из одной функции могут быть вызваны другие функции и т.д., что позволяет спроектировать сложную иерархическую систему функций.
В функции могут быть заданы такие формальные параметры, которые не могут быть в ней изменены. Ля этого используется спецификатор const для формального параметра.
// Попытка изменения константного параметра “а” - const
int Summ0 (const int a , int b, int * sum)
{
a = 5; // НА ДАННОМ ОПЕРАТОРЕ КОМПИЛЯТОР ВЫДАЕТ ОШИБКУ!!!
sum = (a + b);
return *sum;
};
Формальный параметр “a” не может быть изменен в функции. Модификатор const может быть использован для характеристики всей функции, это означает, что данная функция не пожжет изменять данные объекта, но об этом вы больше узнаете в другом курсе.
11 Параметр массив в функции
Массив может быть передан в функцию следующими способами:
-
Фиксированное число элементов в массиве
-
Через указатель на массив и его размер
-
Задание нулевого элемента в конце массива (ограниченное применение)
В некоторых случаях размер массива может быть фиксированным, тогда можно воспользоваться описаниями и вызовами, представленными ниже:
// Заранее фиксировано число элементов в массиве - 5
int Summ51 (int mas[5] , int * psum)
{
int sum = 0 ;
for (int i = 0 ; i < 5 ; i++ )
sum = sum + mas[i];
*psum = sum ;
return *psum; // возвращаемое значение функции
};
…
В основной программе:
int iMas[5] = {1,2,3,4,5}; // 0 - 4
printf ("Сумма в массиве iMas = %d \n" , Summ51 ( iMas, &Sum) );
..
Можно в предыдущем случае использовать для размера массива и #define переменные (переменные этапа компиляции).
При передаче размера массива в качестве параметров описание функции может иметь следующий вид:
// Через указатель на массив и его размер (Razm - формальный параметр)
int Summ5 (int * mas ,int Razm , int * psum)
{
// тело функции
int sum = 0 ;
for (int i = 0 ; i < Razm ; i++ )
sum = sum + mas[i];
*psum = sum ;
return *psum; // возвращаемое значение функции
};
…
В основной программе, размер массива вычисляется динамически (подчеркнуто):
int iMas[5] = {1,2,3,4,5}; // 0 - 4
printf ("Сумма в массиве iMas = %d \n" , Summ5 ( iMas, sizeof(iMas)/sizeof(int) ,&Sum) );
…
Если не предполагается в массиве хранить нулевые элементы, то в конце, массива в качестве ограничителя размера, можно поместить нуль и организовать цикл обработки до первого нуля. Роль нуля может играть и “-1”. Функция имеет вид представленный ниже, отметим, что в этом случае размер не передается.
// Нулевой элемент - конец массива
long Summ6 (int * iMas, long * sum)
{
int i = 0;
while (iMas[i] != 0)
{
*sum = *sum + iMas[i];
i++;
};
return *sum;
}
…
В основной программе для работы алгоритма должно быть:
int iMas[] = {1,2,3,4,5, 0}; // 0 - 4
long SumLong = 0;
printf ("Сумма в массиве iMas = %d \n" , Summ6( iMas, &SumLong) );
Передача размерности может быть выполнена через глобальную переменную, переменную этапа компиляции. Необходимо иметь ввиду что в СИ границы индексов не контролируются. Если массив имеет несколько измерений (например, двумерный массив), то размер каждого измерения передается отдельно.
12 Размещение функций
Описание функции конкретного проекта могут быть размещены в следующих составляющих многомодульной программы:
-
В этом же программном модуле в его начале (до main), в этом случае прототипа функции задавать не надо.
-
В этом же программном модуле в его конце (после main), в этом случае прототип функции задавать обязательно.
-
В другом исходном модуле, прототип должен быть задан обязательно в либо начале главного модуля или либо в подключаемом к нему заголовочном файле.
-
В подключаемом заголовочном файле, прототипа в этом случае задавать не нужно.
При невнимательном описании возможно сообщение компилятора переопределения имен (multiply defined). Качество проекта во многом зависит от того, как грамотно расположены функции в разных модулях и распределены для программирования по различным разработчикам, составляющим команду программистов данного проекта.
13 Рекурсивные функции
В СИ допускается использовать рекурсивные функции, которые могут вызывать сами себя. Наглядно пример рекурсивной функции можно показать при вычисление факториала натурального числа. Описание функции вычисления факториала (в математике - n!):
int fact( int n)
{