Лекция 3. Функции в языке С++. Перегруженные функции. Область действия переменных и связанные с ней понятия. Создание программ из нескольких модулей. Условная компиляция программ. ФУНКЦИИ В ЯЗЫКЕ С++ Функция является основным структурным элементом языка С++. Определение функции имеет следующий синтаксис: возвращаемый_тип имя_функции(список параметров) { тело функции } Пример: long double max_ab(long double a, long double b) {long double x; if (a>b) {x=a} else {x=b}; return x; } Данная функция возвращает значение максимального из своих двух параметров. Оператор return служит для указания возвращаемого функцией значения (в данном случае – значения переменной х). На нём исполнение функции заканчивается. Если оператор return отсутствует, то возвращаемое значение равно 0. Вызов данной функции в программе происходит следующим образом: long double x=2.71, y=3.14, z; z=max_ab(x, y); При вызове функции возвращаемое ею значение можно игнорировать, например: printf(“OK”); Тип параметров функции при вызове должен в точности соответствовать типу параметров, указанных при её объявлении. Помимо определения для функции обычно пишется также её прототип, который размещается в заголовочном файле с расширением *.h и служит для проверки корректности обращений к функциям при компиляции программы из нескольких модулей. Прототип функции идентичен заголовку, но заканчивается точкой с запятой, а тело функции отсутствует: long double max_ab(long double a, long double b); Функция в С++ может иметь переменное число параметров. В этом случае за последним обязательным параметром в заголовке функции следует многоточие. Подобной функцией является, например, функция printf(). Следует также отметить, что в С++, в отличие от некоторых других языков программирования нет понятия “вложенных функций”. Все функции в С++ имеют одинаковый ранг. ПЕРЕГРУЖЕННЫЕ ФУНКЦИИ Иногда возникает потребность в функциях, выполняющих одни и те же действия с различными типами данных, например, вычисление максимума из двух чисел и вычисление максимума из двух символов (в смысле алфавитного порядка). Для удобства в С++ имеется возможность использования перегруженных имён функций, когда функции с одним именем можно идентифицировать по списку параметров, то есть контексту, в котором имя употребляется. Пример: long double max_ab(long double a, long double b) {long double x; if (a>b) {x=a} else {x=b}; return x; } char max_ab(char a, char b) {char x; if (a>b) {x=a} else {x=b}; return x; } Теперь функцию max_ab можно вызывать как с вещественными параметрами, так и с параметрами типа char. Перегруженные функции являются, по сути, различными функциями, идентифицируемыми не только по имени, но и по списку параметров. Отметим, что допусти- мо перегружать функции, отличающиеся только типом параметров. Если функции отличаются только типом возвращаемого значения, то их перегружать нельзя. ОБЛАСТЬ ДЕЙСТВИЯ ПЕРЕМЕННЫХ И СВЯЗАННЫЕ С НЕЮ ПОНЯТИЯ Переменные в С++ могут быть локальными и глобальными, статическими и автоматическими, регистровыми, внешними и нестабильными. Они различаются областью действия, видимостью и временем жизни. Областью действия переменной называется та часть программы, где переменная доступна для программного кода. По своей области действия переменные делятся на локальные и глобальные. Локальные переменные объявляются внутри функции и вне её не доступны. К ним можно обращаться только в пределах объявляющей их функции. Если две функции описывают локальную переменную с одним именем, то это две совершенно различные переменные и никакой неоднозначности не возникает. (Параметры в определении функции можно рассматривать в этом смысле как локальные переменные, инициализируемые значениями аргументов при вызове). В противоположность локальным, глобальные переменные не относятся ни к какой функции и объявляются совершенно независимо. В пределах текущего модуля имя глобальной переменной должно быть уникальным. Областью действия глобальных переменных является (в принципе) вся программа. Если имя локальной переменной функции совпадает с именем глобальной, то локальная переменная в этом случае скрывает глобальную переменную с тем же именем (говорят ещё, что глобальная переменная становится невидимой внутри тела функции). Для придания переменным некоторых специфических свойств в С++ существуют следующие модификаторы переменных: static. Если локальная переменная объявлена в функции как static, то она будет сохранять своё значение между вызовами этой функции (то есть, фактически, существовать всё время, пока программа выполняется). Если модификатор static применить к глобальной переменной, то он ограничит область её действия текущим модулем компиляции. auto. Специфицирует переменную как создаваемую автоматически и предполагается по умолчанию. register. Рекомендует компилятору разместить локальную переменную в регистре процессора, если это возможно. extern. Служит указанием компилятору, что переменная является внешней, то есть объявлена в другом модуле. volatile. Служит для отметки нестабильных переменных. Пример: extern long double pi; long double circle_square(long double r) {long double s; s=pi*r*r; return s; } В данном примере объявлена функция circle_square, вычисляющая площадь круга. Для вычисления она использует переменную pi, которая объявлена в другом модуле программы. СОЗДАНИЕ ПРОГРАММЫ ИЗ НЕСКОЛЬКИХ МОДУЛЕЙ Для создания в среде Turbo C++ Explorer программы из нескольких модулей следует подключить тексты нужных модулей к проекту. Если модули создаются заново, следует выбрать в главном меню опции File|New|Unit. Если подключается уже созданный модуль, нужно выбрать в главном меню Project|Add to project и указать нужный файл. Следует также создать для каждого модуля заголовочные файлы *.h, куда помещать прототипы описанных в модулях функций, чтобы сделать их видимыми для других модулей программы с помощью директивы #include”*.h”. (См. также пример программы к лекции 3). УСЛОВНАЯ КОМПИЛЯЦИЯ КОДА Я зык С содержит специальные средства, позволяющие производить выборочную компиляцию различных участков кода в зависимости от оценки некоторого константного выражения или определения идентификатора. Для этого служат директивы #if, #elif, #else, #endif, #ifdef, #ifndef,. Общая форма применения директив компилятора следующая: #if выражение_1 оператор_1 #elif выражение_2 оператор_2 #elif выражение_3 оператор_3 #else оператор_4 #endif Группа операторов оператор_1 компилируется, если выражение_1 истинно; в противном случае группа оператор_1 опускается. Группа операторов оператор_2 компилируется, если выражение_1 ложно, и выражение_2 истинно, и т.д. Группа оператор_4 компилируется, если все условные выражения ложны. Конструкция условной компиляции должна заканчиваться директивой #endif. Разделы #elif и #else могут отсутствовать. В директивах имеются очень полезные директивы #define и #undef, позволяющие определить и разопределить макроопределение. Они позволяют проверить, определён ли некоторый символ: #define TEST …. #ifdef TEST оператор_1 #else оператор_2 #endif В данном примере, если компилятор прошёл по первой строке (#define TEXT), то он откомпилирует группу операторов оператор_1, а если нет – группу операторов оператор_2. Условная компиляция кода применяется на практике для предотвращения включения файлов, переключения разделов кода и выдачи отладочных диагностических сообщений. Предотвращение включения файлов. Иногда при использовании заголовочных файлов может происходить дублирование кода из-за повторного включения некоторого файла. Чтобы предотвратить повторное включение кода заголовочного файла можно организовать контроль, поместив в заголовочный файл следующие строки (первые две – в начало, последнюю – в конец файла): #ifndef HEADERO_H #define HEADERO_H … … #endif Переключение разделов кода Директивы условной компиляции могут использоваться для простого переключения между двумя различными вариантами кода – старым и обновленным алгоритмом, например. Это можно сделать так: #define NEW_VER I #if NEW_VER // Экспериментальный код. #else // Старый код. #endif Отладочные диагностические сообщения При отладке больших и сложных программ можно с большой пользой применять макросы, генерирующие операторы вывода различных сообщений (например, значений переменных, о начале и завершении функции и т.д.) #define DEBUG_INFO 1 …… #if DEBUG_INFO printf(“Function 1 started\n”); #endif ЗАДАНИЕ К ЛЕКЦИИ 3 Написать функции, вычисляющие периметр и площадь треугольника по значениям длин трёх его сторон. Разместить их в отдельном модуле (отличном от того, где располагается функция main( )). Ввести с консоли значения длин сторон треугольника (с проверкой на корректность неравенства треугольника) и выдать на консоль вычисленные с помощью написанных функций значения его периметра и площади.