Студопедия.Орг Главная | Случайная страница | Контакты | Мы поможем в написании вашей работы!  
 

Тема 3.8 Функции



А теперь нам предстоит разобраться с вопросом о том, как в Си/Си++ реализуется механизм подпрограмм. Вспомним о том, что в Паскале существуют две разновидности подпрограмм: подпрограммы-процедуры и подпрограммы-функции. Тот и другой тип подпрограмм описывается внутри основной программы и компилируется вместе с ней. Реализация механизма подпрограмм в Си/Си++ существенно отличается от аналогичной реализации в Паскале.

Определение функции. Обращение к функции. В Си используется лишь один тип подпрограмм — функция. Здесь вообще не принято употреблять термин «подпрограмма», потому что функция является основной программной единицей в Си, минимальным исполняемым программным модулем. Всякая программа обязательно включает в себя основную функцию с именем main. Если в программе используются и другие функции, то они выполняют роль подпрограмм. Рассмотрим пример. Требуется составить программу нахождения наибольшего значения из трех величин — max (я, Ь, с). Для ее решения можно использовать вспомогательный алгоритм нахождения максимального значения из двух, поскольку справедливо равенство: max (a, b, с) = max (max (A, b), с). Вот программа решения этой задачи с использованием вспомогательной функции.

Тип функции — это тип возвращаемого функцией результата.

Если функция не возвращает никакого результата, то для нее указывается тип void.

Имя функции — идентификатор, задаваемый программистом или main для основной функции.

Спецификации параметров — это либо «пусто», либо список имен формальных параметров функции с указанием типа для каждого из них.

Тело функции — это либо составной оператор, либо блок. Здесь мы впервые встречаемся с понятием блока. Признаком блока является наличие описаний программных объектов (переменных, массивов и т.д.), которые действуют в пределах этого блока. Блок, как и составной оператор, ограничивается фигурными скобками.

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

return; или return выражение;

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

Формат обращения к функции (вызова функции) традиционный:

имя_функции(список_фактических_праметров)

Однако в Си обращение к функции имеет своеобразную трактовку:

обращение к функции — это выражение. В этом выражении круглые скобки играют роль знака операции, для которой функция и фактические параметры (аргументы) являются операндами. Приоритет операции «скобки» самый высокий (см. табл. 4.2), поэтому вычисление функции в выражениях производится раньше других операций. Между формальными и фактическими параметрами при вызове функции должны соблюдаться правила соответствия по последовательности и по типам. Фактический параметр — это выражение того же типа, что и у соответствующего ему формального параметра. Стандарт языка Си допускает автоматическое преобразование значений фактических параметров к типу формальных параметров. В Си++ такое преобразование не предусмотрено. Поэтому в дальнейшем мы будем строго следовать принципу соответствия типов. Необходимо усвоить еще один важнейший принцип, действующий в Си/Си++: передача параметров при вызове функции происходит только по значению. Если снова проводить аналогию с Паскалем, то это значит, что в Си допустимы только параметры-значения (без var). Поэтому выполнение функции не может изменить значения переменных, указанных в качестве фактических параметров. Правило соответствия по количеству, обязательное в Паскале, в Си в некоторых случаях может не соблюдаться. Более того, в Си возможны функции с переменным числом параметров. Примером таких функций являются библиотечные функции p r i n t f () и scanf ().

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

Здесь использован прототип функции. Прототипом называется предварительное описание функции, в котором содержатся все необходимые сведения для правильного обращения к ней: имя и тип функции, типы формальных параметров. В прототипе имена формальных параметров указывать необязательно (как это сделано в примере 2), хотя их указание не является ошибочным. Можно было написать и так, как в заголовке определения функции:

i n t MAX(int x, i n t у);

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

А теперь сопоставим программу на Паскале для вычисления наибольшего общего делителя для суммы, разности и произведения двух чисел с аналогичной программой на Си++.

У читателя может возникнуть вопрос: если основная часть программы является функцией, то кто (или что) ее вызывает? Ответ состоит в следующем: программу вызывает операционная система при запуске программы на исполнение. И в принципе main-функция совсем не обязательно должна иметь тип void. Например, она может возвращать операционной системе целое значение 1 в качестве признака благополучного завершения программы и 0 — в «аварийном» случае. Обработка этих сообщений будет осуществляться системными средствами.

Использование библиотечных функций. Библиотечными называются вспомогательные функции, хранящиеся в отдельных файлах. Стандартные библиотеки входят в стандартный комплект системы программирования на Си/Си++. Кроме того, программист может создавать собственные библиотеки функций. Ранее уже говорилось о том, что для использования стандартных функций необходимо подключать к программе заголовочные файлы соответствующих библиотек. Делается это с помощью директивы претранслятора #include с указанием имени заголовочного файла. Например, #include <stdio.h>. Все заголовочные файлы имеют расширение h (от английского header). Теперь должно быть понятно, что эти файлы содержат прототипы функций библиотеки. На стадии претрансляции происходит подстановка прототипов перед основной функцией, после чего компилятор в состоянии контролировать правильность обращения к функциям. Сами программы, реализующие функции, хранятся в форме объектного кода и подключаются к основной программе на стадии редактирования связей (при работе компоновщика). Рассмотрим программу решения следующей задачи: зная декартовы координаты вершин выпуклого четырехугольника, вычислить его площадь (рис. 2).

Математическое решение этой задачи следующее. Обозначим координаты вершин четырехугольника так: (х\,у\), (х2,у2), (хЗ, уЪ), (х4, уА). Площадь четырехугольника можно вычислить как сумму площадей двух треугольников. В свою очередь, площадь каждого треугольника вычисляется по формуле Герона. Для применения

В этой программе используются функции из трех стандартных библиотек с заголовочными файлами iostream.h, math.h и conio. h. С первыми двумя мы уже встречались раньше. Третья библиотека (файл conio. h) содержит функции, предназначенные для управления выводом на экран в символьном режиме. Она является аналогом модуля CRT в Турбо Паскале. В программе из этой библиотеки используется функция c l r s c r () — очистка экрана.

Еще одним новым элементом в приведенной программе является строка

typedef double D;

Служебное слово typedef представляет собой спецификатор типа, позволяющий определять синонимы для обозначения типов. В результате в рассматриваемой программе вместо длинного слова double для обозначения того же самого типа можно употреблять одну букву D. Данное описание действует глобально и распространяется как на основную, так и на вспомогательные функции. Обратим внимание на еще одно обстоятельство. В функции Geron имеются обращения к функции Line, а в основной функции — обращение только к функции Geron. Для компилятора важно, чтобы перед вызывающей функцией присутствовал или прототип, или определение вызываемой функции. Поэтому если из данной программы убрать прототип функции Line, то ошибки не будет. Но если одновременно с этим поменять местами определения функций Line и Geron, то компилятор выдаст сообщение об ошибке.

Рекурсивные определения функций. Как и в Паскале, в языках Си/Си++ допускается рекурсивное определение функций. Проиллюстрируем определение рекурсивной функции на классическом примере вычисления факториала целого положительного числа.

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

Передача значений через глобальные переменные. Областью действия описания программного объекта называется часть программы, в пределах которой действует (учитывается) это описание. Если переменная описана внутри некоторого блока, то она локализована в этом блоке и из других блоков, внешних по отношению к данному, «не видна». Если описание переменной находится вне блока и предшествует ему в тексте программы, то это описание действует внутри блока и называется глобальным. Глобальная переменная «видна» из блока. Например:

Переменная х является глобальной по отношению к функциям f u n d, main и, следовательно, может в них использоваться. В функциях f u n d и main имеются локальные переменные с одинаковым именем у. Однако это разные величины, никак не связанные друг с другом. Поскольку переменная х является общей для обеих функций, то они могут взаимодействовать через х друг с другом. При обращении к функции передача значений возможна как через параметры, так и через глобальные переменные. Используя этот механизм, в программах на Си можно реализовывать функции, работающие подобно процедурам в Паскале. В следующей программе решается уже рассматриваемая нами задача получения наибольшего из трех значений.

Результат выполнения функции MAX заносится в глобальную переменную z, которая «видна» также и из основной функции. Поэтому при втором обращении эта переменная играет одновременно роль аргумента и результата. Здесь оператор обращения к функции выглядит подобно обращению к процедуре в Паскале, а глобальная переменная z играет роль var-параметра.

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

• автоматическая (ключевое слово auto);

• внешняя (extern);

• статическая (s t a t i c);

• регистровая (r e g i s t e r).

Под глобальные переменные выделяется место во внешней памяти

(не нужно думать, что речь идет о магнитной памяти; это оперативная

память класса extern). Глобальную переменную можно объявить либо вне программных блоков, либо внутри блока с ключевым словом extern. Обычно это делается в тех случаях, когда программный модуль хранится в отдельном файле и, следовательно, отдельно компилируется. Пусть, например, основная и вспомогательная функции хранятся в разных файлах.

Здесь обмен значениями между основной и вспомогательной функцией f unc () происходит через общую глобальную переменную var, для которой во время компиляции выделяется место во внешнем разделе памяти. В результате выполнения данной программы на экран выведется число 50.

Локальные переменные, объявленные внутри блоков, распределяются в автоматической памяти, работающей по принципу стека.

Выделение памяти происходит при входе выполнения программы в блок, а при выходе из блока память освобождается. Ключевое слово auto писать необязательно (подразумевается по умолчанию). Статическая память выделяется под переменные, локализованные внутри блока, но в отличие от автоматической памяти не освобождается при выходе из блока. Таким образом, при повторном вхождении в блок статическая переменная сохраняет свое прежнее значение. Пример объявления статической переменной:

{ s t a t i c int schet=10;...}

Инициализация статической переменной происходит только при первом вхождении в блок. Если инициализация явно не указана, то переменной автоматически присваивается нулевое начальное значение. Статические переменные можно использовать, например, для организации счетчика числа вхождений в блок. Регистровая память выделяется под локальные переменные. Регистры процессора — самый быстрый и самый маленький вид памяти. Они задействованы при выполнении практически всех операций в программе. Поэтому возможность распоряжаться регистровой памятью лучше оставить за компилятором. Наконец рассмотрим пример, в котором используются различные способы описания переменных.





Дата публикования: 2015-02-18; Прочитано: 503 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!



studopedia.org - Студопедия.Орг - 2014-2024 год. Студопедия не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования (0.011 с)...