Вівторок, 19.03.2024, 08:40
Гость

Мішатронік

Мобільна версія | Додати у вибране  | Мій профіль | Вихід | RSS |
Меню сайту
Наше опитування
Чи знаєте вы Java
Всього відповідей: 3
Статистика

Онлайн всього: 1
Гостей: 1
Користувачів: 0


Создание и использование функций

Принципы программирования на языке Си основаны на понятии функции. Мы уже рассмотрели несколько функций: printf( ),scanf( )getchar( )putchar( ). Эти функции являются системными, однако мы создали и несколько своих собственных функций под общим именем main( ). Выполнение программы всегда начинается с команд, содержащихся в функции main( ), затем последняя вызывает другие функции. Рассмотрим вопрос, как создавать свои собственные функции и делать их доступными для функции main( ), а также для других функций.

 

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

 

Почему мы пользуемся функциями? Во-первых, они избавляют нас от повторного программирования. Если конкретную задачу в программе необходимо выполнить несколько раз, мы напишем соответствующую функцию только один раз, а затем будем вызывать ее всегда, когда требуется. Во-вторых, мы можем применять одну функцию, например putchar( ), в различных программах.

 
! Если некоторая задача выполняется только в одной программе, лучше оформить ее решение в виде функции, т.к. функцииповышают уровень модульности программы и, следовательно, облегчают ее чтение, внесение изменений и коррекцию ошибок. Дополнительное преимущество указанного подхода заключается в том, что если мы создадим функции общего вида, то их можно будет использовать и в других программах.
 

Многие программисты думают о функции, как о "черном ящике". Они задают ее через поступающую информацию и полученные результаты. Все, что происходит внутри черного ящика, их не касается. Что нам требуется знать о функциях? Нужно знать, как их можно определять, как к ним обращаться и как устанавливать связи между функцией и программой, ее вызывающей.

 

Абстракция управления в языке Си обеспечивается с помощью функций. Все функции могут быть рекурсивными. В языке Сиотсутствуют подпрограммы (процедуры), однако возврат функцией значения в вызывающую программу не обязателен, следовательно, функции могут быть разделены на две категории - функции, возвращающие значения, и функции, не возвращающие значения в вызывающую программу (подпрограммы) .

 

Определение функций, возвращающих значение, имеет следующий формат:

 
[static] 
тип-результата имя-функции (формальные аргументы)
описание формальных параметров
{
 тело функции
}
 

где имя функции - правильный идентификатор, а тело функции имеет вид

 
определения и описания
операторы
 
! Все, что взято в квадратные скобки, может и не быть static - мы рассмотрим в лекции 10. Указание типа-результата функции в языке Си не является обязательным. Если тип результата не указан, то предполагается, что результат имеет тип int. Поскольку указание типа функции приводит к большей ясности и легкости чтения программы, а также упрощает нахождение в ней ошибок, тип функции всегда должен быть указан явно.
 

В качестве результата функция не может возвращать массив (см. лекцию 13) или функцию, но может возвращать указатель намассив или функцию. Выполнение функции, возвращающей значения, обязано завершиться оператором return вида

 
return e;
 

который обеспечивает выдачу результата eФункция, возвращающая значение, может содержать более одного оператораreturn.

 

Определения функции, не возвращающей значения, имеют следующий формат:

 
[static] void имя-функции(формальные аргументы)
описание формальных параметров
{
 тело функции
}
 

Выполнение такой функции завершается, если выполнено ее тело или оператор return вида

 
return;
 

Функция, не возвращающая значения, может содержать более одного оператора return.

 

Класс памяти static (необязательный) ограничивает видимость функции и других внешних определений. Функция с классом памяти static невидима вне содержащего ее файла. Если в тексте программы есть обращение к функции, то необходимо описание функции, которое в тексте должно быть помещено раньше ее определения. Описания функции имеют следующую форму:

 
[static или extern] тип-результата имя-функции( );
[static или extern] void имя-функции( );
 

Если в описании не указан класс памяти (см. лекцию 10) , то по умолчанию, предполагается extern.

 
!

Для компиляторов, не способных обрабатывать тип void, программист может определить тип void как

 
#define void int
 

и использовать его для определения функций, не возвращающих значения. Рекомендуется следовать этому соглашению для улучшения ясности программы. Однако в таких случаях компилятор будет не в состоянии обнаружить некорректное использование этих функций для возврата значений, поскольку на самом деле рассматриваемые функции возвращают значения, и эти значения имеют тип int.

Аргументы функции

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

 

Если для связи с некоторой функцией требуется более одного аргумента, то наряду с именем функции можно задать список аргументов, разделенных запятыми. Например:

 
void print_num(i,j)
int i,j;
{
 printf("значение i=%d. Значение j=%d.", i,j);
}
 

Обращение в программе к данной функции будет таковым:

 
print_num(6,19);
 

Особое внимание нужно уделить правилам передачи аргументов при обращении к функциямСинтаксис языка Си предусматривает только один способ передачи аргументов - передачу по значениям. Это означает, что формальные параметры (аргументы) функциилокализованы в ней, то есть недоступны вне определения функции и никакие операции над формальными параметрами в телефункции не изменяют значения фактических параметровПередача параметров по значению предусматривает следующие действия:

 
  • При компиляции функции выделяются участки памяти для формальных параметровФормальные параметры оказываются внутренними объектами функции. При этом для параметров типа float формируются объекты типа double. Для параметров типа charshort int создаются объекты типа int. Если параметром является массив, то формируется указатель на начало этого массива и он служит представлением массива-параметра в теле функции (об указателе массивов описано в 12 лекции данного курса).
  • Вычисляются значения выражений, использованных в качестве фактических параметров при вызове функции.
  • Значения выражений- фактических параметров заносятся в участки памяти, выделенные для формальных параметров функции. При этом float преобразуется в double, a charshort int - в тип int.
  • В теле функции выполняется обработка с использованием значений внутренних объектов-параметров, и результат передается в точку вызова функции как возвращаемое ею значение.
  • Никакого влияния на фактические параметры функция не оказывает.
 

Возвращение значений

Напишем функцию, вычисляющую абсолютную величину числаАбсолютная величина числа - это его значение, если отбросить знак. Например, абсолютная величина 125 - это 125, а абсолютная величина числа ( -125 ) - это тоже 125. Назовем этуфункцию abs( ). Входом для этой функции будет любое число, для которого мы хотим найти абсолютную величину. Выходная величина возвращается при помощи ключевого слова языка Си - return. Поскольку функция abs( ) должна быть вызвана другойфункцией, мы создадим простую функцию main( ), основной целью которой будет проверка, работает ли функция abs( ).Программа, спроектированная для того, чтобы проверить работу функции именно таким образом, называется драйверомДрайверподвергает функцию последовательным проверкам. Если результаты оказываются удовлетворительными, то ее можно поместить в программу, заслуживающую большего внимания. Термин драйвер обычно относится к программам, управляющим работой устройств:

 
/*драйвер*/
int main( )
{
 int a=100, b=0, c=-122;
 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; 
 /*возвращает значение y вызывающей программы*/
 return(y);
}
 

Результат работы программы выглядит так:

 
100 0 122
 

Ключевое слово return указывает на то, что значение выражения, заключенного в круглые скобки, будет присвоено функции, содержащей это ключевое слово (оператор). Поэтому, когда функция abs( ) впервые вызывается драйвером, значением abs(a)будет число 100, которое затем присваивается переменной d.

 

Переменная y является внутренним объектом функции abs( ), но значение y передается в вызывающую программу с помощью оператора return. Действие, оказываемое оператором

 
d=abs(a);
 

по другому можно выразить так:

 
abs(a);
d=y;
 

Такой записью мы воспользоваться не можем! Вызывающая программа даже не подозревает о том, что переменная y существует.

 

Оператор return оказывает и другое действие. Он завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Это происходит даже в том случае, если оператор return является не последним оператором телафункции:

 
/* Функция, вычисляющая абсолютную величину числа, вторая версия */
int abs(int x)
{
 if(x < 0)
 return(-x);
 else
 return(x);
}
 

Эта версия программы проще. Для пользователя обе версии неразличимы, т. к. у них имеется один и тот же вход, и они обеспечивают один и тот же выход. Только внутренние структуры обеих функций различны.

 
/* третья версия функции abs( ) */
int abs(int x)
{
 if(x < 0) return(-x);
 else return(x);
 printf("Работа завершена!\n");
}
 

Наличие оператора return препятствует тому, чтобы оператор печати когда-нибудь выполнился в программе.

 

Можно пользоваться оператором

 

Єдина Країна! Единая Страна!