Лабы / C++.Ашарина / C_function / functionC++
.htmМОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОННОЙ ТЕХНИКИ МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОННОЙ ТЕХНИКИ
(ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ)
Кафедра “Информатика и программное обеспечение вычислительных систем”
Учебная дисциплина “Информатика”
ЛАБОРАТОРНАЯ РАБОТА
Прогаммирование задач с использованием функции на языке С++.
Цель работы: изучить правила описания и получить навыки использования
функций при написании программ на языке С++.
Функция – логически завершенный, определенным образом оформленный фрагмент программы, имеющий имя. Функции позволяют разделить большие вычислительные задачи на более мелкие.
Функции в С++ бывают встроенные (стандартные) и пргаммируемые пользвателем.
Стандартные функции.
Стандартные (библиотечные) функции описаны со своими прототипами в одном или нескольких заголовочных файлах, включаемых в программу директивой include и представляют собой мощную поддержку языка С++ для решения задач, связанных с вводом/выводом, работой со строками и файлами, математическими вычислениями и многим другим.
Вызов функции приводит к выполнению некоторых действий. Например, при обращении к функции printf() осуществляется вывод данных на экран. Другие функции вычисляют некоторую величину, используемую затем в программе. Например, функция scanf() позволяет получить некоторые данные, вводимые с клавиатуры, а функция printf() – вывести необходимую информацию на экран:
scanf (“%f”, &x); {функция приема числа с клавиатуры}
y = sin(x); {функция вычисления синуса}
pintf (“%f”, y); {функция выдачи результатов на экран}
В общем случае функции могут выполнять действия и получать значения величин, используемых в программе.
Определение функции.
В общем виде функции описываются следующим образом:
[<тип >] <имя функции> (<тип > <имя параметра>,…, <тип > <имя параметра>)
{
<тело функции>
}
Функции, программируемые пользователем.
избавляет нас от повторного программирования;
одну и ту же функцию можно применять в различных программах;
повышают уровень модульности программы, следовательно, облегчают ее чтение, внесение изменений и коррекцию ошибок.
Пусть нам нужно написать программу, которая:
вводит набор чисел;
сортирует эти числа;
находит их среднее значение;
выводит на печать гистограмму.
Основная программа в этом случае может иметь вид:
void main()
{
float list[50];
readlist(list); // функция ввода массива
sort(list); // функция сортировки массива
average(list); // функция нахждения среднего значения
bargraph(list); // функция вывода на экран гистограммы
}
Очевидно, при такой записи предполагается, что перечисленные функции тоже запрограммированы.
Для начала создадим функцию, которая печатает 65 символов “*” в ряд. Чтобы эта функция выполнялась в некотором контексте, она включена в программу печати фирменного бланка.
Программа состоит из функций: main() и starbar().
// Фирменный бланк
#include <stdio.h>
#define Limit 65
void main()
{
starbar();
printf (“Moscow Institute Electronic Engineering \n”);
starbar();
}
// Далее следует функция starbar()
void starbar()
{
int count;
for (count=1; count <= Limit; count++)
putchar (‘*’);
putchar (‘\n’);
}
Мы рассмотрели пример простейшей функции, не имеющей аргументов и не возвращающей никаких значений.
Каждая программа на языке С обязательно содержит функцию с именем main (главная), которая являетсяосновным телом программы. Для всех остальных функций, если они присутствуют в программе следует объявлять прототипы – схематические записи, которые сообщают компилятору имя и форму каждой функции в программе.
Синтаксис для прототипа функции с параметром:
возвращаемый_тип или void имя_функции (список параметров с указанием типов);
Рассмотрим на примере использование параметров функции. Напишем функцию space, в качестве аргумента которой будет число пробелов, которое должна напечатать эта функция.
#define address “Zelenograd”
#define name “Moscow Institute Electronic Engineering”
#define department “Informatics and Programming”
//Определение функции starbar
#include <stdio.h>
#define LIMIT 65
void starbar()
{
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 (‘ ’);
}
void main()
{
int spaces;
starbar();
space(25);
printf(“%s \n”, address);
spaces = (65-strlen(name))/2; //Вычислить, сколько пробелов
space(spaces);
printf(“%s \n”, name);
space((65-strlen(department))/2); //аргумент-выражение
printf(“%s \n”, department);
starbar();
}
Переменная number называется формальным аргументом. Указанная переменная приобретает значения фактического аргумента при вызове функции.
Другими словами, формальный аргумент – переменная в вызываемой подпрограмме, а фактический аргумент – конкретное значение, присвоенное этой переменной вызывающей программой.
Если для связи с некоторой функцией требуется более одного аргумента, то наряду с именем функции можно задать список аргументов, разделенных запятыми:
void printnum (int i, int j)
{
printf(“координаты точек %d%\n”, i, j);
}
Входная величина функции может обрабатываться благодаря наличию аргумента; выходная величина возвращается при помощи ключевого слова return.
Если предполагается, что функция будет возвращать некоторое значение, то перед именем функции вместо слова void следует указать тип возвращаемого значения.
Пример:
void main()
{
int a=10, b=0, c=-22;
int d, e , f;
d = abs(a);
e = abs(b);
f = abs(c);
printf (“%d %d %d \n”, d, e, f);
}
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;
printf(“А вот эта строка не напишется никогда и ни за что,” \n);
printf(“так как этому препятствует наличие оператора return.” \n);
}
Применение оператора return без указания значения приводит к тому, что функция, в которой он содержится, завершает свое выполнение и управление возвращается в вызывающую функцию, а вследствие того, что у данного оператора отсутствует возвращаемое выражение, никакое значение в вызывающую функцию не передается.
Тип функции определяется типом возвращаемого ею значения, а не типом ее аргументов.
Если указание типа отсутствует, о по умолчанию считается, что функция имеет тип int. Если значение функции не принадлежит типу int, то необходимо указать ее тип в двух местах: там, где она определяется, и там, где она используется.
Если функция не возвращает никакого результата посредством выполнения оператора return выражение, то ее тип определяется как void (в том числе и для функции main()). Иными словами, тип функции не определен.
Запрещается в С++ определять функции внутри других функций.
Связь между функциями определяется через аргументы, возвращаемые значения и внешние переменные. В исходном файле функции разрешается располагать в любом порядке; исходную программу можно разбивать на любое число файлов, лишь бы ни одна из функций не оказалась “разрезанной”.
Однако, если текст функции находится в том же файле, где и вызывающая функция, причем если текст вызываемой функции расположен в файле раньше, чем текст вызывающей функции, то описание прототипа необязательно.
Рассмотренные раньше функции содержали объявления переменных внутри определения функции. такие переменные называются локальными и доступны только в пределах функции, в которой они определены. В следующем примере переменная var1 доступна только внутри функции func(), а var2 активна только в main().
#include <stdio.h>
void func(void)
{
int var1= 11; //локальная переменная var1
printf(“var1=%d\n”, var1);
}
int main(void)
{
int var2=22; //локальная переменная var2
printf(“var2=%d\n”, var2);
func();
printf(“var1=%d\n”, var1); //приведет к возникновению ошибки
return 0; // ‘Undefined symbol var1’
}
Переменная, объявленная вне любой из функций, называется глобальной переменной и доступна в области от точки ее объявления до конца файла. Чтоюы сделать var1 доступной как в func(), так и в main(), следует описать эу переменную в начале файла:
#include <stdio.h>
int var1= 11; //локальная переменная var1
void func(void)
{
printf(“var1=%d\n”, var1);
}
int main(void)
{
int var2=22;
printf(“var2=%d\n”, var2);
func(); // на экран будут выданы
printf(“var1=%d\n”, var1); // два одинаковых значения.
return 0;
}
Аргументы функций.
В языке С++ аргументы функции передаются только по значению, т. е. вызванная функция получает свою собственную временную копию каждого аргумента, а не его адрес. Это значит, что функция не может непосредственно изменять сам оригинальный аргумент в вызвавшей ее функции.
Чтобы вызванная функция могла изменять некоторую переменную в вызывающей функции, а также возвращать несколько значений, необходимо передать функции не переменные, а их адреса. При этом формальные параметры, соответствующие передаваемым адресам, должны быть описаны как указатели на переменные данного типа.
В следующем примере приведена программа, в которой вводятся значения целых переменных a и b и с помощью функции swp() происходит их обмен между собой.
#include <stdio.h>
void main()
{
int a, b;
puts(“Введите значения a и b”);
scanf(“%d%d”,&a, &b);
swp(&a, &b);
printf(“Измененные значения: a=%d; b=%d”,a,b);
}
swp(x, y)
int *x, *y; // x и y – указатели, *x и *y – значения, на которые они указывают
{
int c;
c = *x;
*x = *y;
*y = c;
}
Рассмотрим передачу массивов в качестве аргументов функции. Для передачи одномерного массива достаточно следующих действий. Объявим функцию, которая принимает в качестве параметра массив значений типа 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.
Пример. Передача функции многомерных массивов.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define 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(); // rand() – генератор случайных чисел
}
void DisplayTable (int data [] [COLS], int numRows)
{
int r, c;
for (r=0; r<numRows; r++)
{
printf(“\n”);
for (c=0; c<COLS; c++)
printf(“%8d”, data [r][c]);
}
printf(“\n”);
}
Для дальнейшего рассмотрения массивов в качестве аргументов функции, необходимо ввести понятие указателя.
Указатель- это переменная, содержащая адрес другой переменной.
Указателями в С++ являются имена массивов. Имя массива указвает на первый элемент массива, индекс которого в С++ равен нулю.
При работе с указателями используются два оператора:
“&” – унарный оператор определения адреса переменной,
“*” - унарный оператор раскрытия ссылки.
Записывая функцию С++, которая передает параметр в виде массива, можно оъявить этот параметр как указатель на базовый тип массива.
Общий синтаксис для прототипа функции с параметром в ивде указателя на массив:
возвращаемый_тип функция (базовый_тип*, <другие_типы_параметров>);
Общий синтаксис для определения этой функции:
возвращаемый_тип функция (базовый_тип *массив_параметр, <другие параметры>)
Напишем функцию, использующую массивы, а затем перепишем ее, применяя указатели.
Рассмотрим функцию, которая находит среднее значение массива целых чисел. На входе функции мы имеем имя массива и количество его элементов. На выходе получаем среднее значение, которое передается при помощи оператора return. Оператор вызова функции может выглядеть следующим образом:
printf(“Среднее из заданных значений %d \n”, mean (numbs. sizes)); /* Находит
среднее значение массива из 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
{
printf(“нет массива. \n”);
return 0;
}
}
Перепишем эту же программу с использованием указателей. Объявим pnt_arr указателем на тип int и заменим элемент массива array[index] на соответствующее значение: *(pnt_arr+index).
// Использование указателей для нахождения среднего значения массива
// n целых чисел.
int mean(int *pnt_arr, int n)
{
int index;
long sum;
if (n>0)
{
for (index=0, sum=0; index<n; index++)
sum+ = *(pnt_arr+index);
return sum/n; // возвращает целое sum/n.
}
else
{
printf(“нет массива. \n”);
return 0;
}
}
При такой замене в вызове функции ничего менять не придется, т.к. имя массива является указателем. Поэтому можно присваивать указателю адрес первого элемента, используя просто имя массива.
int iarray [10];
int *iptr = iarray; // То же самое, что iptr = &iarray[0].
В С++ указатели можно увеличивать или уменьшать. указатель, увеличенный на 3 будет указывать на четвертый элемент массива (эквивалентно &iarray[3]). Другими словами, увеличивая указатель на единицу, мы в действительности увеличиваем адрес, который он представляет, на размер объекта связанного с ним типа.
Т. к. указателям типа void не соответсвует никакой тип данных, к ним нельзя применять арифметические операции.
Указатель можно индексировать точно также, как массив. Компилятор в действительности преобразует индексацию в арифметику указателей: iarray[3]=10; представляется как *(iarray+3)=10;
В С++ имеется несколько видоизмененная форма указателя, называемая ссылкой. Ссылка на некоторую переменную может рассматриваться как указатель, который при употреблении всегда разыменовывается. Однако, для ссылки не требуется дополнительного пространства в памяти: она является просто другим именем (псевдонимом) переменной.
Для определения ссылки применяется унарный оператор &.
Пример.
#include <stdio.h>
int value = 10;
int &refval = value; //Ссылка на value
int main(void)
{
printf(“value=%d\n”, value);
refval+ =5; // Модификация через ссылку
printf(“value=%d\n”, value);
printf(“Адрес value равен %p\n”, &value);
printf(“Адрес refval равен %p\n”, &refval);
return 0;
}
Хотя ссылка может быть определена как псевдоним переменной, прежде всего, ссылки применяются для параметров функций и для типов возвращаемых функциями значений. Вызов функции, ожидающей в качестве параметра ссылочную переменную, синтаксически не отличается от вызова, в котором параметр передается значением. Чтобы указать, что функция ожидает предачу параметра ссылкой, в прототипе перед именем переменной ставится модификатор &. Рассмотрим пример использования параметров-ссылок:
#include <stdio.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;
printf(“J = %d\n”,j);
inc_val(j);
printf(“После inc_val(j) J = %d\n”,j);
inc_ptr(&j);
printf(“После inc_ptr(j) J = %d\n”,j);
inc_ref(j);
printf(“После inc_ref(j) J = %d\n”,j);
return 0;
}
При вызове функции, ожидающей передачи ссылки, компьютер неявно передает адрес специфицированного параметра. Внутри самой функции компилятор транслирует все обращения к параметру, разыменовывая переланный адрес. При этом:
вызывающей функции не требуется применять операцию взятия адреса (&);
вызываемая функция избавляет от использования косвенных операций(*);
Ссылка может быть использована как тип, возвращаемый функцией. Это позволяет присваивать функции значение.
Пример. Ссылка как возвращаемый функцией тип.
#include <stdio.h>
const int array Size = 0xF;
static int valArray [array Size];
int &valueAt(int indx)
{
return valArray[indx];
}
int main(void)
{
for (int i =0; i<arraySize; i++)
valueAt(i) =1<<j;
for (i = 0; i<arraySize; i++)
printf(“Значение [%02d]=%-6d \n”, i, valueAt(i));
return 0;
}
Функцию valueAt(int) можно применять как для чтения элемента с определенным индексом, так и для присваивания нового значения этому элементу.
Рассмотрим теперь двумерный массив как параметр функции.
Описание двумерного массива a из 2 строк и 4 столбцов в списке параметров функции может задано одним из следующих способов:
int fun (int a[2][4]);
int fun (int a[][4]);
int fun (int a(*[4]));
Последний из приведенных способов означает, что параметр функции есть указатель на массив из четырех значений типа int.
Обращение к функции fun может быть следующим:
y = fun(a); //передается адрес
y = fun(&a[0][0]); // начала массива
y = fun(a+i); //передается адрес i-го элемента массива.
Приведенный ниже пример иллюстрирует различные варианты обращения к элементам двумерного массива и варианты использования двумерного массива в качестве параметра функции.
#include <stdio.h>
#include <conio.h>
void main()
{
int a [2][4] = {{1, 2, -8, -7}, {-1, 2, 8, 7}};
*pa = *a; //или *pa = &a [0][0];
int i, j, x1, x2, x3, x4, x5, *pi;
int fun(int (*)[ ]); // или fun(int (*a[ ])); или int fun(int a[ ][ ]);
// или int fun(int a[2][4]); - описание прототипа функции fun
i =1; j=2;
pi = &a [i][j];
// далее следует эквивалентные с точки зрения результата операторы;
// в результате их выполнения получим x1=x2=x3=x4=x5=8
x1 = a[i][j]; x2 = pi; x3 = *(pa+i*4+j); x4 = *(*a+i*4+j); x5 = pa[i*4+j];
clrscr;
printf (“x1=%3d; x2=%3d; x3=%3d; x4=%3d; x5=%3d \n”,x1, x2, x3, x4, x5);
printf (“x0=%3d \n”, fun(a)); // x0 = a[0][0];
for (i=0; j<4; j++)
printf (“x=%3d \n”, a[i][j])); // -1 –2 8 7
for (i=0; j<4; j++) // в функцию fun передается адрес
printf (“x=%3d \n”, fun(&a[i][j])); // -1 –2 8 7
// другой способ доступа к элементам двумерного массива
pa = *a + 4;
for (j=0; j<4; j++)
printf (“x=%3d \n”, fun(pa+j)); // -1 –2 8 7
int fun (int a[ ][4]); // или fun (int(*a)[4]);
{return a[0][0];}
}
Требования к отчету.
Отчет должен содержать:
название и цель работы;
краткие теоретические сведения;
задание для варианта задания, соответствующего номеру Фамилии студента в группе (если студент закреплен за определенной ЭВМ, имеющей номер, то номеру ЭВМ);
схему алгоритма для задачи своего варианта;
текст программы для задачи своего варианта;
результаты выполнения программы.
Контрольные вопросы.
Каков допустимый уровень вложенности функции в языке С?
Как определить функцию? Что такое прототип функции? Всегда ли обязательно объявление прототипов?
Как передать информацию фцнкции?
В чем разница между формальными и фактическими аргументами? Где описываются аргументы?
Где описываются локальные переменные функции?
Для чего служит оператор return? Обязательно ли его использование?
Все ли правильно в следующем определении функции:
hallo (num)
{
int num, count;
for (count=1; count<=num; num++)
printf (“Hallo, my friend! \n”);
} ?
ВАРИАНТЫ ЗАДАНИЙ.
Задать значения вещественным элементам массивов
и вычислить
2. Задать значения целочисленным элементам массивов
и вычислить
3. Задать значения целочисленным элементам массивов
и вычислить
4. Задать значения вещественным элементам массивов
и вычислить
5. Задать значения целочисленным элементам матриц
где i = 1,2,3,4; j = 1,2,…,7 и сформировать массивы C и D, состоящие из
максимальных элементов столбцов матриц A и B соответственно.
6. Задать значения вещественным элементам массивов
где i = 1,2,…,9 и вычислить элементы массивов
по формулам:
Функция f вычисляется по формуле
с точностью ε =0,001.
7. Задать значения целочисленным элементам матриц
где i = 1,2,…,8; j = 1,2,…,6 и сформировать массивы C и D, состоящие из
максимальных элементов строк матриц M и N соответственно.
8. Задать значения вещественным элементам матриц
где i = 1,2,…,7; j = 1,2,…,5 и сформировать массивы B и R, состоящие из
минимальных элементов строк матриц A и Q соответственно.
9. Задать значения целочисленным элементам матриц
где i = 1,2,…,5; j = 1,2,…,8 и сформировать массивы R и T, состоящие из
отрицательных элементов строк матриц P и Q соответственно.
10. Задать значения вещественным элементам матриц
где i = 1,2,…,6; j = 1,2,…,6 и сформировать массивы X и Y, из положительных элементов строк матриц C и D соответственно.
11. Задать значения целочисленным элементам матриц
где i = 1,2,3; j = 1,2,…,8 и сформировать массивы T и S соответственно из
элементов матриц W и Z, больших заданного числа P.
12. Задать значения вещественным элементам матриц
где i = 1,2,…,7; j = 1,2,3 и сформировать массивы Y и Z соответственно из
элементов матриц B и D, меньших заданного числа R.
13. Задать значения вещественным элементам матриц
где i = 1,2,…,5; j = 1,2,…,8 и сформировать массивы R и T, состоящие из
минимальных элементов столбцов матриц P и Q соответственно.
14. Задать значения целочисленным элементам массивов
где i = 1,2,…,6 и вычислить элементы массивов
по формулам:
Функция f вычисляется по формуле
с точностью ε =0,01 (|t|>1).
15. Задать значения вещественным элементам массивов
где i = 1,2,…,6 и вычислить
16. Задать значения целочисленным элементам массивов
где i = 1,2; j = 1,2,3 и вычислить элементы матриц
по формулам:
Для вычисления интеграла использовать метод Симпсона, описанный в лабораторной работе №3.
17. Задать значения целочисленным элементам массивов
где i = 1,2,…,5; j = 1,2,3 и вычислить элементы массивов
по формулам:
Для вычисления интеграла использовать метод Симпсона, описанный в лабораторной работе №3.
18. Задать значения целочисленным элементам массивов
где i = 1,2,…,10 и сформировать массив N = {n[i]}, i-ый эоемент которого равен числу корней уравнения sin x = 1/x на интервале a[i]<x<b[i]. Для определения числа корней уравнения составить программу.
19. Сформировать массив X из корней уравнений
и найти максимальный из этих корней. Для решения уравнений использовать метод деления отрезка пополам, описанный в лабораторной работе №3.
20. Задать значения вещественным элементам массивов
и вычислить
если
21. Задать значения вещественным элементам четырех квадратных матриц
5-го порядка и вычислить квадрат той из матриц, укоторой наибольшая
сумма диагональных элементов.
22. Задать значения целочисленным элементам матриц
где i = 1,2,3; j = 1,2,…,5 и вычислить величины
где x[i], y[i] –максимальные элементы i-ых строк матриц A и B
соответственно.
23. Задать целочисленные значения элементам квадратных матриц
и векторам (матриц-столбцам)
где i = 1,2,…,4; j = 1,2,…,4 и вычислить вектор W = AX + BY – CZ.
24. Задать значения 9-элементам вещественным векторам X, Y, Z и вычислить
величину D = (A,A) – (B,C), где A обозначает тот из векторов X, Y, Z, в котором наименьший максимальный элемент, B и C обозначают два других вектора, а (V1,V2) – скалярное произведение векторов V1 и V2.