Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Лабораторная работа № 8
Программирование с использованием функций
Цель работы: 1) изучить правила описания функций; 2) приобрести навыки использования функций при написании программ на языке C++.
Теоретические сведения
Основным модулем программ в языке С++ является функция.
Функция - логически завершенный, определенным образом оформленный фрагмент программы, имеющий имя. Функции позволяют разделить большие вычислительные задачи на более мелкие.
Каждая программа на языке С++ обязательно содержит функцию с именем main (главная), которая является телом программы. Для всех остальных функций, если они присутствуют в программе, следует объявлять прототипы - схематические записи, которые сообщают компилятору имя и форму каждой функции в программе.
Синтаксис для прототипа функций с параметрами:
тип_возвращаемого_значения имя_функции (список_параметров_с_указанием_типов);
Функции в С++ бывают стандартные (библиотечные) и программируемые пользователем.
Описания стандартных функций находятся в файлах, включаемых в программу с помощью директивы #include. Такие файлы называют заголовочными; они имеют расширение h.
Обращение к имени функции в основной программе называется вызовом функции.
Вызов функций приводит к выполнению некоторых действий или вычислению некоторой величины, используемой затем в программе.
y = sin (x); //функция вычисления синуса
В общем виде функции определяются следующим образом:
тип_возвращаемого_значения имя_функции (тип имя_параметра,...,тип имя_параметра)
{
тело_функции
}
Функции, которые программист создает сам, упрощают процесс написания программ, поскольку они:
Пример 8.1. Создадим функцию, которая печатает 65 символов "*" в ряд. Чтобы эта функция выполнялась в некотором контексте, она включена в программу печати фирменного бланка. Программа состоит из функций: main() и stars().
// Фирменный бланк
#include <iostream.h>
const int Limit=65;
void stars(void); // прототип функции stars()
void main()
{ stars();
cout<<"Moscow Institute of Electronic Engineering"<<endl;
stars();
}
// Определение функции stars()
void stars()
{ int count;
for (count=1; count<=Limit; count++)
putchar ('*');
putchar ('\n');
}
Мы рассмотрели пример простейшей функции, не имеющей аргументов и не возвращающей никаких значений.
Параметры функций
Рассмотрим на примере использование параметров функции.
Пример 8.2. Напишем функцию space(), в качестве аргумента которой будет число пробелов, которое должна напечатать эта функция.
#define address " Zelenograd"
#define name " Moscow Institute Electronic Engineering "
#define department " Informatics and Programming"
const int LIMIT=65;
#include <iostream.h>
void stars();
void space(int number);
void main()
{ int spaces;
stars();
space(25);
cout<<address<<endl;
spaces=(LIMIT - strlen(name))/2; // Вычислить, сколько
// нужно пробелов
space(spaces);
cout<<name<<endl;
space((LIMIT - strlen(department))/2); // аргумент выражение
cout<<department<<endl;
stars();
}
//Определение функции stars()
void stars()
{ int count;
for (count=1; count<=LIMIT; count++)
putchar ('*');
putchar ('\n');
}
//Определение функции space()
void space(int number)
{ int count;
for (count=1; count <=number; count++)
putchar (' ');
}
Переменная number называется формальным аргументом. Эта переменная приобретает значения фактического аргумента при вызове функции. Другими словами, формальный аргумент - переменная в определении вызываемой подпрограммы, а фактический аргумент - конкретное значение, присвоенное этой переменной вызывающей программой.
Если для связи с некоторой функцией требуется более одного аргумента, то наряду с именем функции можно задать список аргументов, разделенных запятыми:
void printnum (int i, int j)
{ cout<<"Координаты точек”<< i << j <<endl; }
Входная величина функции может обрабатываться благодаря наличию аргумента; выходная величина возвращается при помощи ключевого слова return.
Возвращение значений с помощью оператора return
Если функция должна возвращать в вызывающую программу некоторое значение, то перед именем функции вместо слова void следует указать тип возвращаемого значения.
Пример 8.3.
int abs(int x); // прототип функции abs();
void main()
{ int а=10, b=0, c= -22;
int d, e, f;
d= abs(a);
e= abs(b);
f= abs(c);
cout<<d<<e<<f<<endl;
}
int abs(int x) // функция, вычисляющая абсолютное
// значение числа
{ int y;
y= (x<0) ? x : x;
return y; // возвращает значение у вызывающей программе
}
Запись y= (x<0) ? x : x эквивалентна записи
if (x<0) y= x;
else y= x;
Другая версия функции abs(x):
int abs(int x)
{ if (x<0) return x;
return x;
}
Третья версия функции abs(x):
int abs(int x)
{ if (x<0) return x;
return x;
cout<<"А вот эти строки не напечатаются никогда и ни за что";
cout <<endl;
cout<<"так как этому препятствует наличие оператора return.";
cout <<endl;
}
Применение оператора return без указания значения приводит к тому, что функция, в которой он содержится, завершает свое выполнение и управление возвращается в вызывающую функцию, а вследствие того, что у данного оператора отсутствует возвращаемое выражение, никакое значение в вызывающую функцию не передается.
Тип функции определяется типом возвращаемого ею значения, а не типом ее аргументов.
В языке C, если указание типа отсутствует, то по умолчанию считается, что функция имеет тип int. В языке C++ такое умолчание приводит к трудно обнаруживаемым ошибкам, поэтому надежнее явно указывать тип функции.
Если функция не возвращает никакого результата посредством выполнения оператора return выражение, то тип возвращаемого ею значения определяется как void (в том числе и для функции main ()). Иными словами, тип функции не определен.
Запрещается в C++ определять функции внутри других функций.
Связь между функциями определяется через аргументы, возвращаемые значения и внешние переменные. В исходном файле функции разрешается располагать в любом порядке; исходную программу можно разбивать на любое число файлов, лишь бы ни одна из функций не оказалась "разрезанной".
Если текст функции находится в том же файле, где и вызывающая функция, причем если текст вызываемой функции расположен в файле раньше, чем текст вызывающей функции, то описание прототипа необязательно. Но описание прототипа является хорошим стилем программирования.
Рассмотренные выше функции содержали объявления переменных внутри определения функции. Такие переменные называются локальными и доступны только в пределах функции, в которой они определены. В следующем примере переменная var1 доступна только внутри функции func(), а var2 активна только в main().
Пример 8.4.
#include <iostrem.h>
void func(void)
{ int var1=11; //локальная переменная var1
cout<<"var1="<<var1<<endl;
}
int main(void)
{ int var2=22; //локальная переменная var2
cout<<"var2="<<var2<<endl;
func();
cout<<"var1="<<var1<<endl; //приведет к возникновению
//ошибки
return 0; // 'Undefined symbol var1'
}
Переменная, объявленная вне любой из функций, называется глобальной переменной и доступна в области от точки ее объявления до конца файла. Чтобы сделать var1 доступной как в func(), так и в main(), следует описать эту переменную в начале файла.
Пример 8.5.
#include <iostream.h>
int var1=11; // глобальная переменная var1
void func(void)
{ cout<<"var1="<<var1<<endl;
}
int main(void)
{ int var2=22;
cout<<"var2="<<var2<<endl;
func(); //на экран будут выданы
cout<<"var1="<<var1<<endl; //два одинаковых значения.
return 0;
}
Параметры функций
Передача массивов в качестве аргументов функции
Рассмотрим передачу массивов в качестве аргументов функции. Для передачи одномерного массива достаточно следующих действий. Объявим функцию, которая принимает в качестве параметра массив значений типа double и вычисляет сумму элементов этого массива. Прототип этой функции можно записать следующим образом:
double SumOfData(double data[max]);
Еще лучше объявить
double SumOfData(double data[ ], int n);
Тогда при объявлении функции SumOfData() ей можно передать массив аргументов вместе с целым числом n, сообщающим, сколько элементов содержится в передаваемом массиве.
Сама функция SumOfData() может содержать, например, обычный цикл while, суммирующий элементы массива, а функция return возвращает результат:
double SumOfData(double data[ ], int n)
{ double sum=0;
while (n>0)
sum += data [--n];
return sum;
}
При передаче двумерных массивов необходимо сообщить компилятору количество столбцов для вычисления адреса элементов в начале каждой строки.
Пусть ROWS - количество строк, COLS - количество столбцов. Объявим функцию с массивом в качестве параметра:
void AnyFn(int data[ROWS][COLS]);
или
void AnyFn(int data[ ][COLS]);
Можно передать параметр, определяющий количество строк:
void AnyFn(int data[ ][COLS], int numRows);
где numRows сообщает функции число строк в массиве data.
Пример 8.6. Передача функции многомерных массивов
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
const int COLS=8;
void FillArray(int data[ ][COLS], int numRows);
void DisplayTable(int data[ ][COLS], int numRows);
int data1[7][COLS];
int data2[4][COLS];
void main()
{ randomize(); // инициализация генератора случайных чисел
FillArray(data1, 7);
DisplayTable(data1, 7);
FillArray(data2,4);
DisplayTable(data2,4);
}
void FillArray(int data[ ][COLS], int numRows)
{ int r, c;
for (r=0; r<numRows; r++)
for (c=0; c<COLS; c++)
data[r][c]= rand(50); // rand() - генератор случайных чисел
}
void DisplayTable(int data[ ][COLS], int numRows)
{ int r, c;
for (r=0; r<numRows; r++)
{ cout<<endl;
for (c=0; c<COLS; c++)
cout<<setw(5)<<data[r][c]);
}
cout<<endl;
}
Понятие об указателях
Указатель - это переменная, содержащая адрес другой переменной.
Указателями в С++ являются, в частности, имена массивов. Имя массива указывает на первый элемент массива, индекс которого в С++ равен нулю.
При работе с указателями используются два оператора:
"&" - унарный оператор определения адреса переменной,
"*" - унарный оператор раскрытия ссылки.
Записывая функцию С++, которая передает параметр в виде массива, можно объявить этот параметр как указатель на базовый тип массива.
Общий синтаксис для прототипа функции с параметром в виде указателя на массив:
возвращаемый_тип имя_функции (базовый_тип*, другие_типы_параметров);
Общий синтаксис для определения этой функции:
возвращаемый_тип имя_функции (базовый_тип *массив_параметр, другие_параметры)
{
// тело_функции
}
Напишем функцию, использующую массивы, а затем перепишем ее, применяя указатели.
Чтобы вызванная функция могла изменять некоторую переменную в вызывающей функции, а также возвращать несколько значений, необходимо передать функции не переменные, а их адреса. При этом формальные параметры, соответствующие передаваемым адресам, должны быть описаны как указатели на переменные данного типа.
В следующем примере приведена программа, в которой вводятся значения целых переменных a и b и с помощью функции swp() происходит их обмен между собой.
#include <iostream.h>
swp(int *x, int *y);
void main()
{ int a, b;
cout<<»Введите значения a и b»;
cin>>a>>b;
swp(&a, &b);
cout<<»Измененные значения: a=»<<a<<”b=”<<b;
}
void swp(int *x, int *y)
// x и y указатели, *x и *y значения, на которые они указывают
{ int c;
c=*x;
x=y;
*y=c;
}
cout<<»Среднее из заданных значений»<<mean (numbs, size)<<endl;
/* Находим среднее значение массива из n целых чисел.*/
int mean (int array [ ], int n)
{ int index;
long sum; /* Если массив содержит много элементов, сумма может оказаться довольно большой, поэтому используем тип long int */.
If (n>0)
{ for (index=0, sum=0; index<n; index++)
sum+=array [index];
return sum/n; // Возвращает int.
}
else
{ cout<<”Нет массива.”<<endl;
return 0;
}
}
Перепишем эту же программу с использованием указателей. Объявим iPtr указателем на тип int и заменим элемент массива array[index] на соответствующее значение: *(iPtr+index).
Int mean (int *iPtr, int n)
{ int index;
long sum;
if (n>0)
{ for (index=0, sum=0; index<n; index++)
sum+=*(iPtr+index);
return sum/n; // возвращает целое sum / n
}
else
{ cout<<”Нет массива”<<endl;
return 0;
}
}
При такой замене в вызове функции ничего менять не придется, так как имя массива и указатель в данном случае взаимозаменяемы. Поэтому можно присваивать указателю адрес первого элемента, используя просто имя массива.
Int iarray [10];
int *iPtr = iarray; // То же самое, что iPtr=&iarray[0].
В С++ указатели можно увеличивать или уменьшать. Указатель, увеличенный на 3, будет указывать на четвертый элемент массива (эквивалентно &array[3]). Другими словами, увеличивая указатель на единицу, мы в действительности увеличиваем адрес, который он представляет, на размер объекта связанного с ним типа.
Так как указателям типа void не соответствует никакой тип данных, к ним нельзя применять арифметические операции.
Указатель можно индексировать точно так же, как массив. Компилятор в действительности преобразует индексацию в арифметику указателей: iarray[3]=10; представляется как *(iarray+3)=10;
Понятие ссылки
В С++ имеется несколько видоизмененная форма указателя, называемая ссылкой. Ссылка на некоторую переменную может рассматриваться как указатель, который при употреблении всегда разыменовывается. Однако для ссылки не требуется дополнительного пространства памяти: она является просто другим именем (псевдонимом) переменной.
Для определения ссылки применяется унарный оператор &.
Пример 8.10.
# include <iostream.h>
int value=10;
int &refval=value; // Ссылка на value
int main (void)
{ cout<<”value=”<<value<<endl;
refval+=5; // Модификация через ссылку
cout<<”value=”<<value<<endl;
cout<<”Адрес value равен “<<&value<<endl;
cout<<”Адрес refval равен “<<&refval<<endl;
return 0;
}
Хотя ссылка может быть определена как псевдоним переменной, прежде всего ссылки применяются для параметров функций и возвращаемых функциями значений. Вызов функции, ожидающей в качестве параметра ссылочную переменную, синтаксически не отличается от вызова, в котором параметр передается значением. Чтобы указать, что функция ожидает передачу параметра ссылкой, в прототипе перед именем переменной ставится модификатор &. Рассмотрим пример использования параметров-ссылок.
Пример 8.11.
# includе <iostream.h>
void Inc_val (int i) // Получает параметр-значение
{ i++; // Модификация не влияет на оригинал
}
void Inc_ptr (int *i) // Получает адрес оригинала
{(*i)++; // Модифицирует оригинал путем косвенной адресации
}
void Inc_ref (int & i) // Получает параметр-ссылку
{i++; // Модифицирует оригинал!
}
int main(void)
{ int j=10;
cout<<”j=”<<j<<endl;
Inc_val (j);
cout<<”После Inc_val (j) j = “<<j<<endl;
Inc_ptr (&j);
Cout<<”После Inc_ptr (j) j=”<<j<<endl;
Inc_ref (j);
Cout<<”После Inc_ref (j) j=”<<j<<endl;
return 0;
}
При вызове функции, ожидающей передачи ссылки, компьютер неявно передает адрес специфицированного параметра. Внутри самой функции компилятор транслирует все обращения к параметру, разыменовывая переданный адрес. При этом:
Ссылка может быть использована как тип, возвращаемый функцией. Это позволяет присваивать функции значение.
Пример 8.12. Ссылка как возвращаемый функцией тип.
#include <iostream.h>
const int arraySize = 0xF;
static int valArray[arraySize];
int &valueAt(int indx)
{ return valArray [indx];
}
int main (void)
{ for (int I=0; I<arraySize; I++)
valueAt (i)=1<<I;
for (I=0; I<arraySize; I++)
cout<<”Значение”<<I<<”=”<<valueAt(i)<<endl;
return 0;
}
Функцию valueAt(int) можно применять как для чтения элемента с определенным индексом, так и для присваивания нового значения этому элементу.
Контрольные вопросы
2. Как определить функцию? Что такое прототип функции? Всегда ли обязательно объявление прототипов?
3. Как передать информацию функции?
4. В чем разница между формальными и фактическими аргументами? Где описываются аргументы?
5. Где описываются локальные переменные функции?
6. Для чего служит оператор return? Обязательно ли его использование?
7. Проверьте, все ли правильно в следующем определении функции:
hallo (num)
{ int num, count;
for (count =1; count <= num; num++)
cout<<”Hello, my friend!”<<endl;
}
На оценку "3" нужно выполнить свой вариант из табл. 8.1, на "4" и "5" из табл. 8.2.
Все логически завершенные фрагменты программ оформить в виде функций.
Варианты заданий
Номер |
Задание |
1, 15 |
Если да, то Заменить его минимальный элемент данного целочисленного массива из 15 элементов на сумму предшествующих элементов, а максимальный элемент на сумму последующих элементов. |
2, 16 |
Создать массив, состоящий из 20 целочисленных значений. Создать новый массив из номеров элементов исходного массива, имеющих нулевое значение, и сжать исходный массив, удалив все нулевые элементы. |
3, 17 |
В массиве из 15 вещественных чисел определить, сколько раз изменяется знак и вывести номера позиций, в которых происходит смена знака. |
4, 18 |
В массиве из 20 целых чисел найти среднее арифметическое значение элементов, попадающих во введенный с клавиатуры интервал. |
5, 19 |
В массиве из 20 целых чисел найти сумму и количество чисел, находящихся между минимальным и максимальным элементами, включая и сами эти числа. |
6, 20 |
Не используя других массивов переставить элементы 10-элементного символьного массива в обратном порядке. |
7, 21 |
В массиве из 15 вещественных чисел (положительных и отрицательных) определить наибольшее количество последовательно расположенных положительных чисел. |
8, 22 |
Создать массив из 20 символьных значений. Сформировать из его значений три других массива: состоящих из цифр, из букв и из символов, не являющихся ни буквами, ни цифрами. |
9, 23 |
Заменить повторяющиеся элементы исходного 15-элементного массива, состоящего из целых чисел, нулевыми значениями. |
10, 24 |
Объединить два упорядоченных по возрастанию целочисленных массива (М1[10] и M2[5]) в один, не нарушая упорядоченности. |
11, 25 |
Дан массив int mas[15]. Создать другой массив, поместив в него сначала элементы массива, стоящие на четных местах, затем - стоящие на нечетных местах. |
12, 26 |
Заменить нечетные элементы исходного 20-элементного массива, состоящего из целых чисел, нулевыми значениями. |
13, 27 |
Объединить два упорядоченных по алфавиту символьных массива (М1[10] и M2[5]) в один, не нарушая упорядоченности. |
14, 28 |
Дан массив char mas[10]. Создать другой массив, поместив в него сначала цифровые элементы массива, затем буквенные (считаем, что в массиве содержатся только алфавитно-цифровые символы). |
Номер варианта |
Задание |
1, 21 |
Задать значения вещественным элементам массивов А = {ai| i = 0,1,…,7}, B = {bjj = 0,1,…,5}, C = {ck k = 0,1,…,9}, D = {dnn = 0,1,…,9} и вычислить |
2, 22 |
Задать значения целочисленным элементам массивов А = {aj| i = 0, 1,…, 8}, B = {bjj = 0, 1,…,5}, C = {ck k = 0,1,…,6} и вычислить |
3, 23 |
Задать значения целочисленным элементам массивов М = {mi i = 0, 1,…,7}, L = {ljj = 0, 1,…,6}, C = {ck k = 0, 1,…,8} и вычислить |
4, 24 |
Задать значения вещественным элементам массивов А = {ai i = 0, 1,…, 6}, B = {bjj = 0, 1, 2, 3}, C = {ck k = 0, 1, 2,…, 10} и вычислить |
5, 25 |
Задать значения целочисленным элементам матриц А = {aij}, B = {bij}, где i = 0, 1, 2, 3; j = 0, 1, 2,…, 6 и сформировать массивы С и D, состоящие из максимальных элементов столбцов матриц А и В соответственно. |
6, 26 |
Задать значения вещественным элементам массивов A = {ai} и B = {bi}, где i = 0, 1, 2,…,9 и вычислить элементы массивов X = {xi} и Y = {yi} по формулам:
|
7, 27 |
Задать значения целочисленным элементам матриц M = {mij} и N = {nij}, где i = 0, 1, 2,…, 7; j = 0, 1, 2,…, 5 и сформировать массивы С и D, состоящие из максимальных элементов строк матриц M и N соответственно. |
8, 28 |
Задать значения вещественным элементам матриц А = {аij} и Q = {qij}, где i = 0, 1, 2,…, 6; j = 0, 1, 2,…, 4 и сформировать массивы B и R, состоящие из минимальных элементов столбцов матриц A и Q соответственно. |
9, 29 |
Задать значения целочисленным элементам матриц P = {pij} и Q = {qij}, где i = 0, 1, 2,…, 4; j = 0, 1, 2,…, 7 и сформировать массивы R и T из сумм отрицательных элементов строк матриц P и Q соответственно. |
10, 30 |
Задать значения вещественным элементам матриц С = {сij} и D = {dij}, где i = 0, 1, 2,…, 5; j = 0, 1, 2,…, 5 и сформировать массивы X и Y из произведений положительных элементов столбцов матриц C и D соответственно. |
11, 31 |
Задать значения целочисленным элементам матриц W = {wij} и Z = {zij}, где i = 0, 1, 2; j = 0, 1, 2,…, 7 и сформировать массивы T и S соответственно из элементов матриц W и Z, больших заданного числа Р. |
12, 32 |
Задать значения вещественным элементам матриц B = {bij} и D = {dij}, где i = 0, 1, 2,...,7; j = 0, 1, 2, 3, и сформировать массивы Y и Z, состоящие соответственно из элементов матриц B и D, меньших заданного числа R. |
13, 33 |
Задать значения вещественным элементам матриц Р = {рij}, Q = {qij}, где i = 0, 1, 2,..., 5; j = 0, 1, 2,..., 8 и сформировать массивы R и T, состоящие из минимальных элементов столбцов матриц P и Q соответственно. |
14, 34 |
Задать значения целочисленным элементам матриц A = {aij}, B = {bij}, где i = 0, 1, 2,..., 5; j = 0, 1, 2, 3 и вычислить элементы массивов X = {xij}, Y = {yij} по формулам:
|
15, 35 |
Задать значения вещественным элементам массивов A = {ai}, B = {bi}, C = {ci}, где i = 0, 1, 2,...,6 и вычислить |
16, 36 |
Задать значения целочисленным элементам матриц A = {aij}, B = {bij}, где i = 0, 1; j = 0, 1, 2 и вычислить элементы матриц Y = {yij}, Z = {zij} по формулам |
17, 37 |
Задать значения целочисленным элементам массивов С = {сi} и D = {di}, где i = 0, 1, 2,..., 6 и вычислить элементы массивов S = {si} и T = {ti}, где i = 0, 1, 2,..., 6, по формуле:
|
18, 38 |
Задать значения вещественным элементам массивов X = {xi| i = 0, 1, 2,..., 5}, Y = {yj| j = 0, 1, 2,..., 7}, Z = {zk| k = 0, 1, 2,..., 9}, и вычислить если |
19, 39 |
Задать значения вещественным элементам четырех квадратных матриц 5х5 и вычислить сумму квадратов значений той из матриц, у которой наибольшая сумма диагональных элементов. |
20, 40 |
Задать значения целочисленным элементам матриц А = {аij}, B = {bij}, где i = 0, 1, 2, 3, 4 j = 0, 1, 2, 3 и вычислить величины S=x0/x4+ x1/x3 + ... +x4/x0, T= y0/y4+ y1/y3+ ... + y4/y0, где xi, yi - максимальные элементы i-х строк матриц А и В соответственно. |
89
PAGE 89