Создание и использование функций
Разработка и отладка алгоритмов и программ с применением пользовательских функции.
Цель. Научиться разрабатывать алгоритмы и программы с применением пользовательских функций, осуществлять выбор способа обмена данными.
Основные теоретические сведения
Создание и использование функций
Принципы программирования на языке Си основаны на понятии функции. Уже были рассмотрены некоторые функции: printf( ), scanf( ), getchar( ), putchar( ). Эти функции являются системными, однако мы создали и несколько собственных функций под общим именем main( ). Выполнение программы всегда начинается с команд, содержащихся в функции main( ), затем последняя вызывает другие функции. Рассмотрим вопрос, как создавать свои собственные функции и делать их доступными для функции main( ), а также для других функций.
Функция – это самостоятельная единица программы, спроектированная для реализации конкретной задачи. Вызов функций приводит к выполнению некоторых действий. Например, при обращении к функции printf( ) осуществляется вывод данных на экран. Функции могут выполнять различные действия и получать значения величин, используемых в программе.
Использование функций избавляет от многократного «переписывания» кода. Если конкретную задачу в программе необходимо выполнить несколько раз, можно написать соответствующую функцию только один раз, а затем по необходимости вызывать ее кроме того мы можем применять одну функцию, например putchar( ), в различных программах.
Многие программисты думают о функции, как о "черном ящике". Они задают ее через поступающую информацию и полученные результаты. Все, что происходит внутри черного ящика, их не касается. Таким образом, все, что такому программисту нужно знать о конкретной, уже заранее реализованной, функции это: как ее можно определить, как к ней обратиться и как установить связи между функцией и программой, ее вызывающей.
Абстракция управления в языке Си обеспечивается с помощью функций. Все функции могут быть рекурсивными (т.е. вызывать сами себя). В языке Си отсутствуют подпрограммы (процедуры), однако возврат функцией значения в вызывающую программу не обязателен, следовательно, функции могут быть разделены на две категории - функции, возвращающие значения, и функции, не возвращающие значения в вызывающую программу (подпрограмму).
Определение функций, возвращающих значение, имеет следующий простейший формат:
тип-результата имя-функции (формальные аргументы)
{
тело функции
}
Пример:
float f(float x){
float y;
y=x*x;
return y;
}
или
float f(float x){
return x*x;
}
Указание типа-результата функции в языке Си не является обязательным. Если тип результата не указан, то предполагается, что результат имеет тип int. Поскольку указание типа функции приводит к большей ясности и легкости чтения программы, а также упрощает нахождение в ней ошибок, тип функции всегда должен быть указан явно.
В качестве результата функция не может возвращать массив или функцию, но может возвращать указатель на массив или функцию. Выполнение функции, возвращающей значения, обязано завершиться операцией return вида
return e;
которая обеспечивает передачу результата e.
Функция, возвращающая значение, может содержать более одной операции return, однако только в различных ветках операций ветвления, например,
int f(int a){
if (a>5) return a;
else return 0;
}
Если функция не возвращает значения, то в заголовке функции указывается тип void.
Например:
void f(int a){
for (int i=0;i<a;i++)printf("Hello world");
}
В такой функции наличие операции return необязательно. Если return все же присутствует, то после него не должно указываться никаких параметров:
return;
Оператор return оказывает и другое действие. Он завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Это происходит даже в том случае, если оператор return является не последним оператором тела функции:
int abs(int x)
{
if(x < 0) return(-x);
else return(x);
printf("Работа завершена!\n");
}
Наличие оператора return препятствует тому, чтобы оператор печати когда-нибудь выполнился в программе.
Вызов функций
Вызов функции производится следующим образом:
f(b);
или таким
int t = f(b);
В первом случае результат передается системе и не сохраняется в переменной.
Все функции в программе, написанной на языке Си, равноправны. Каждая из них может вызывать любую другую функцию и, в свою очередь, каждая может быть вызвана любой другой функцией. Это делает функции языка Си несколько отличными от процедур Паскаля, поскольку процедуры в Паскале могут быть вложены в другие процедуры, причем процедуры, описанные в разных процедурах, являются недоступными для процедур, описанных в других независимых процедурах.
У функции main есть специфика. Она заключается в том, что после сборки программы, состоящей из нескольких функций, ее выполнение начинается с первого оператора функции main( ).
Параметры функции.
Различают понятия аргументов и результатов функции. Результаты – это возвращаемые функцией данные в основную программу, аргументы – это данные, передаваемые в функцию. Любые данные, которые записываются после имени функции в скобках, будем называть параметрами функции. Совокупность параметров, их типов, а также специализированных ключевых слов будем называть сигнатурой функции.
Формальный параметр – параметр, который описывается в заголовке функции и значение которого считается неопределенным до вызова данной функции. Фактический параметр – параметр, который передается в функцию при ее вызове (под фактическими параметрами мы в данном случае будем подразумевать и константы или сложные выражения). Независимо от типа фактического параметра он вначале вычисляется, а затем его величина передается функции. Фактический параметр - это конкретное значение, которое присваивается переменной, называемой формальным параметром.
Если для связи с некоторой функцией требуется более одного аргумента, то наряду с именем функции можно задать список аргументов, разделенных запятыми. Например:
print_num(int i, int j)
{
printf("значение i=%d значение j=%d", i,j);
}
Обращение в программе к данной функции будет таковым:
print_num(6,19);
либо таковым:
int k = print_num(6,19);
Обратите внимание, аргументы функции при ее объявлении нельзя записать так: print_num(int i, j)
В языке Си допустимы функции, количество аргументов у которых при компиляции функции не фиксировано. Количество и тип аргументов становится известным только в момент вызова функции, когда явно задан список фактических аргументов (параметров). При определении и описании таких функций, имеющих списки параметров неопределенной длины. Спецификация формальных параметров заканчивается запятой и многоточием
Спецификация явных параметров - список спецификации параметров, количество и типы которых фиксированы и известны в момент компиляции. Эти параметры обязательны. Каждая функция с переменным количеством параметров должна иметь хотя бы один обязательный параметр. После списка явных (обязательных) параметров ставится запятая, а затем - многоточие. Компилятор знает, что дальнейший контроль соответствия количества и типов параметров при обработке вызова функции проводить не нужно. Чтобы функция с переменным количеством параметров могла воспринимать параметры различных типов, необходимо в качестве исходных данных каким-то образом передавать ей информацию о типах параметров.
Пример:
#include <stdio.h>
/* Функция суммирует значения своих параметров */
long summa(int m,...) /*m - число параметров*/
{
int *p=&m; /*настроили указатель на параметр m*/
long t=0;
for(;m>=0;m--) t+=*(++p);
return t;
}
void main()
{
printf("\n summa(2,6,4)=%d",summa(2,6,4));
printf("\n summa(6,1,2,3,4,5,6)=%d", summa(6,1,2,3,4,5,6));
Передача параметров в функцию и из нее в языке Си может осуществляться двумя способами:
• по значению
• по указателю
В языке Си++ добавляется еще один способ передачи параметров – по ссылке.
Рассмотрим передачу аргументов и результатов по значению. В этом случае передается не сама переменная, а ее копия, что абсолютно не влияет на значение переменной вне данной функции.
int a = 5;
int f(int a){
a+=3;
return a;
}
int k = f(a);
В данном случае в результате a= 5, k= 8.
Если же осуществлять передачу параметров по указателю, то в данном случае изменяется и содержимое внешней по отношению к функции переменной.
int *a;
int = 5;
void f(int *a){
(*a)+=3;
}
f(&a);
int k = *a;
В данном случае в результате a= 8, k= 8. Обратите внимание, что в функцию необходимо передать адрес переменной (&)
Передача по ссылке является, фактически, упрощенной формой передачи параметра по указателю.
int a = 5;
void f(int &a){
a+=3;
}
f(a);
int k = a;
Обратите внимание, что в данном случае не нужно писать значек «*». Результат, по прежнему, a= 8, k= 8