Лекция 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 отсутствует, то возвращаемое значение равно 1. Вызов данной функции в программе происходит следующим образом: long double x=2.71, y=3.14, z; z=max_ab(x, y); При вызове функции возвращаемое ею значение можно игнорировать, например:
© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС
printf(“OK”); Тип параметров функции при вызове должен в точности соответствовать типу параметров, указанных при её объявлении. Помимо определения для функции обычно пишется также её прототип, который размещается в заголовочном файле с расширением *.h и служит для проверки корректности обращений к функциям при компиляции программы из нескольких модулей. Заголовочный файл подключается к тому модулю, в котором программист желает вызвать функцию директивой #include. Прототип функции идентичен заголовку, но заканчивается точкой с запятой, а тело функции отсутствует: long double max_ab(long double a, long double b); На рисунке показана схема взаимосвязи модулей, функций, прототипов функций и заголовочных файлов.
#include ”unit2.h” void main() { Unit1.c func1(); }
void func1();
Unit2.h
void func1() { ….. }
Unit2.c
Функция в С++ может иметь переменное число параметров. В этом случае за последним обязательным параметром в заголовке функции следует многоточие. Подобной функцией является, например, функция printf( ). Следует также отметить, что в С++, в отличие от некоторых других языков программирования нет понятия “вложенных функций”. Все функции в С++ имеют одинаковый ранг. Перегруженные функции © В.М. Гриняк, доц. каф. ИСПИ ВГУЭС
Иногда возникает потребность в функциях, выполняющих одни и те же действия с различными типами данных, например, вычисление возведение числа в целую степень и возведение числа в дробную степень. Для удобства в С++ имеется возможность использования перегруженных имён функций, когда функции с одним именем можно идентифицировать по списку параметров, то есть контексту, в котором имя употребляется. Пример: long double pow(long double a, long double b) {long double x; // алгоритм возведения числа а в вещественную степень b return x; } long double pow(long double a, int b) {long double x; int i; x=1; for(i=0; i < b; i++) { x=x * a; } return x; } Теперь функцию max_ab можно вызывать как с вещественными параметрами, так и с целым вторым параметром, при этом будут вызываться разные функции. Перегруженные
функции
являются,
по
сути,
различными
функциями,
идентифицируемыми не только по имени, но и по списку параметров. Отметим, что допустимо перегружать функции, отличающиеся только типом параметров. Если функции отличаются только типом возвращаемого значения, то их перегружать нельзя.
© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС
Область действия переменных и связанные с нею понятия Переменные в С++ могут быть локальными и глобальными, статическими и автоматическими, регистровыми, внешними и нестабильными. Они различаются областью действия, видимостью и временем жизни. Областью действия переменной называется та часть программы, где переменная доступна для программного кода. По своей области действия переменные делятся на локальные и глобальные. Локальные переменные объявляются внутри функции и вне её не доступны. К ним можно обращаться только в пределах объявляющей их функции. Если две функции описывают локальную переменную с одним именем, то это две совершенно различные переменные и никакой неоднозначности не возникает. (Параметры в определении функции
можно
рассматривать
в
этом
смысле
как
локальные
переменные,
инициализируемые значениями аргументов при вызове). В противоположность локальным, глобальные переменные не относятся ни к какой функции и объявляются совершенно независимо. В пределах текущего модуля имя глобальной переменной должно быть уникальным. Областью действия глобальных переменных является (в принципе) вся программа. Если имя локальной переменной функции совпадает с именем глобальной, то локальная переменная в этом случае скрывает глобальную переменную с тем же именем (говорят ещё, что глобальная переменная становится невидимой внутри тела функции). Для придания переменным некоторых специфических свойств в С++ существуют следующие модификаторы переменных: 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, которая объявлена в другом модуле программы. На рисунке показана схема взаимосвязи глобальных переменных в программе из нескольких модулей
Unit1.c extern int k; void main() {int m; m=k; }
Unit2.c int k=10; …..
На следующем рисунке показана схема недопустимой взаимосвязи глобальных переменных в программе из нескольких модулей
© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС
Unit1.c extern int k; void main() {int m; m=k; }
Unit2.c
Unit3.c
int k;
int k;
…..
…..
Создание программы из нескольких модулей Для создания в среде Turbo C++ Explorer программы из нескольких модулей следует подключить тексты нужных модулей к проекту – выбрать в главном меню опцию Project|Add to Project. Если модули создаются заново, следует выбрать в главном меню опции 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, расположенный на портале
© В.М. Гриняк, доц. каф. ИСПИ ВГУЭС