Здравствуйте. На третьей лекции вы познакомились с циклами. То есть со способами многократного повторения одного определенного блока кода. Давайте представим себе такую ситуацию. Допустим нам необходимо повторить этот блок кода несколько раз но не в одном месте, а в различных частях программы. Конечно, можно просто написать его несколько раз, более того можно просто этот код скопировать. Но есть способ эффективнее и проще – использовать функции.
5.1. Функции
Когда программы становятся очень большими в коде реально можно потеряться. Единственный способ избежать подобных неприятностей, это разделить программу на несколько частей, и работать с ними по отдельности. Этими частями и будут функции. Такой способ программирования называют Процедурным программированием.(Процедура и функция это одно и тоже). Что же такое функция, по-простому это отдельный блок кода, имеющий определенное назначение, то есть выполняющий какую-то задуманную программистом работу. Вообще-то с функциями мы уже встречались. Вспомните, когда мы работали с генератором случайных чисел, мы получали системное время с помощью функции time(), устанавливали начальное значение генератора с помощью функции srand(), ну а само число получали с помощью rand().
Использование функции можно разделить на три этапа: 1. Объявление функции 2. Определение функции 3. Вызов.
Объявление функции
Объявление сообщает компилятору о существовании некой функции. Вот синтаксис:
Здесь: Тип возвращаемого значения – это тип переменной, которую возвращает функция по завершению своей работы. Как я уже сказал, у каждой функции есть свое назначение, она выполняет определенную работу. Возьмем простой пример – функция умеет суммировать два целых числа. Результат этого сложения и будет возвращен в виде целого числа. Имя функции – это и есть имя. Ведь к функции как то нужно обращаться. К именам функций применимы те же требования что и к именам переменной. Помните главное - имя должно быть понятным. Список аргументов(еще называют параметры функции) - это записанные в скобках передаваемые функции значения, попросту говоря это список переменных над которыми эта функция будет совершать определенные действия. Объявляются эти аргументы так же как и обычные переменные, и дальнейшем используются внутри функции. Несколько аргументов отделяются друг от друга запятыми. Количество аргументов не ограничено, но им может и не быть вовсе. И обращаю ваше внимание, что завершает объявление уже знакомые нам точка с запятой. Пример объявления функций:
Code
void print();
Подобную запись еще называют прототипом функции. Здесь у нас объявлена функция которая не возвращает никакого значения( тип void – пустой тип) и не принимающая ни одного аргумента. Судя из названия, функция умеет что-то там печатать.
Code
int sum(int a, int b);
Это уже немного описанный пример. Функция имеет два аргумента типа int. В результате своей работы она возвращает целочисленное значение (int). При объявлении в списке аргументов функции имя переменной можно не указывать, то есть достаточно только указать тип данных. Вот так:
Code
int sum(int, int);
Здесь имена a и b отсутствуют, но они в прототипе не очень-то и нужны. Функций мы еще наобъявляемся, так что не будем тратить время и двигаемся дальше.
Определение функции
Итак, функцию мы уже объявили. Компилятор уже знает о ее существовании, но понятия не имеет, как она работает. Весь, принадлежащий функции код это и есть определение, по-другому реализация, функции. Синтаксис такой:
Code
Тип_возвращаемого_значения имя_функции(список аргументов) { Блок кода; }
Здесь мы может видеть уже знакомое нам объявление, но без завершающей точки с запятой. Далее в фигурных скобках идет принадлежащий функции код, то есть определение функции. По-моему все просто, поэтому перейду сразу к примеру. Ранее мы объявили функцию sum(), сейчас напишем ее определение.
Code
int sum(int a, int b) { return a + b; }
Аргументы int a, int b используются в функции как обычные переменные. Как мы уже знаем, функция возвращает определенное значение. Для этих целей в примере мы и видим оператор возврата - return. Синтаксис оператора следующий:
Code
return возвращаемое значение;
Возвращаемым значением также может быть и любое выражение, как в нашем примере это a + b. Оператор возврата имеет самый низкий приоритет, то есть будет выполнен только после того, как все выражение будет вычислено. Запомните, тип возвращаемого значения обязательно должен соответствовать типу, указанному в объявлении функции. В одной функции может содержаться несколько операторов return. Чаще всего в следующей конструкции:
Здесь возвращенное значение зависит от заданного условия.
Вызов функции.
Чтобы воспользоваться функцией, необходимо произвести ее вызов в нужном месте программы. Для вызова используется следующий синтаксис:
Code
имя_функции (значение1, значение2, …)
Здесь количество и тип значений должны соответствовать количеству и типу аргументов, объявленных в прототипе вызываемой функции. То есть, для объявленной ранее функции
Code
int sum(int a, int b);
вызов будет таким:
Code
sum(5, 8);
Даже если функция не имеет аргументов, круглые скобки при ее вызове писать обязательно. Кроме чисел функции также можно передавать переменные соответствующего типа:
Code
int x=10; sum(x, 156);
Использование функций.
Ну а теперь напишем небольшую программу, в которой используем все, с чем познакомились ранее.
Code
#include <iostream> using namespace std;
int sum(int, int); //обьявление(прототип) функции
void main() { setlocale(0,"");
int a; int a2; int b; cout<<"Введите первое число "; cin>>a; cout<<"Введите второе число "; cin>>a2; b = sum(a,a2); // вызов функции cout<<"Сума двух чисел "<<b; cin.get(); cin.get(); }
int sum(int a, int b) //определение(реализация) функции { return a + b; }
В этом примере мы видим все, о чем говорили ранее. Объявление, определение и вызов. В строке:
Code
b = sum(a,a2); // вызов функции
возвращенный функцией результат присваивается переменной b.
Пример неплохо иллюстрирует учебный материал, но созданная нами функция бесполезная. Более того, код примера неэффективный и некорректный, поэтому далее, чтобы получше освоить функции и получить еще крупицу опыта, мы его улучшим. Говоря об эффективности, я имел в виду, что в примере есть бесполезные строки кода, ненужное объявление переменных и другое. От всего этого можно и даже нужно избавится, сделав нашу программу компактнее и быстрее(чисто теоретически, поскольку кода мало и повышение производительности никто не заметит.) Теперь о корректности. В программе наличествует ввод данных. Подразумевается, что будет ведено какое-то число. Но что если пользователь случайно введет какое-то левое значение? В программе произойдет сбой. А это не есть хорошо. Поэтому сейчас нам необходимо сделать соответствующую проверку. Поскольку, в случае ввода неверного значения программа должна запросить его повторно и вводить нам нужно несколько значений, получение и проверка данных от пользователя будет производиться внутри одной функции GetDigit(). Вот ее код:
Code
int GetDigit() { bool r=true; char c[20]; cout<<"Введите число "; while (r) // основной цикл { cin>>c; // ввод for(int i=0;c[i]!='\0';i++) // цикл анализа { if(isdigit(c[i])) // проверка цифра или символ { r=false; } else { r=true; cout<<"Повторите ввод (число давай) "; break; // принудительное завершение цикла анализа } } } int i= atoi(c); return i; // возврат числа }
Работает она следующим образом. Вначале запрашивается ввод. Далее, внутри основного цикла, введенные данные присваиваются переменной с, являющейся массивом символов. Даже если пользователь введет корректное число, все равно оно будет записано в памяти как набор символов. Это необходимо для того, чтобы: 1. При вводе символа не произошел сбой(при попытке присвоить числу значение, содержащее не цифры а символы.) 2. Чтобы мы могли выполнить анализ. Далее в еще одном цикле,
Code
for(int i=0;c[i]!='\0';i++) // цикл анализа { if(isdigit(c[i])) // проверка цифра или символ { r=false; } else { r=true; cout<<"Повторите ввод (число давай) "; break; // принудительное завершение цикла анализа } }
до тех пор, пока не встретится конец строки(нулевой символ '\0' ) производится анализ массива. Выполняется проверка каждого элемента на то, является ли он числом или символом. Делается это с помощью стандартной функции isdigit(), возвращающей true если проверяемый символ – цифра. В этом случае анализ продолжается, и если все символы цифры, условие продолжения основного цикла (переменная r) становится ложным.
Если же функция isdigit(), возвращает false, условие продолжения основного цикла вновь становится истинным, выводится предложение повторно ввести число, и цикл анализа принудительно завершается оператором break. Принудительное завершение необходимо потому, что после символа может встретится опять цифра и условие станет ложным, тогда как на самом деле ввод то некорректный.
После завершения основного цикла мы должны возвратить значения.
Поскольку с это массив символов(строка), а нам нужно число, чтобы выполнить соответствующее преобразование, используется еще одна стандартная функция atoi(). (Прототип int atoi(const char *str); Функция принимает указатель на строку, а возвращает число) Она умеет превращать последовательность символов в число, при условии что символы – цифры. Иначе будет возвращен 0. Здесь нужно быть осторожным, поскольку можно запутаться, ведь строка тоже может быть «0». (Можете сами добавить соответствующую проверку, я этого делать не буду, а ниже приведу более сложный вариант функции GetDigit().)
Code
int i= atoi(c); return i; // возврат числа
здесь с – имя массива, по совместительству являющееся также и указателем.
Эту запись можно сократить, ведь абсолютно незачем использовать лишнюю переменную
Надеюсь это уяснили. Теперь внесем в нашу программу изменения, задействуем созданную нами функцию GetDigit(). Не забудьте добавить ее объявление. Теперь функция main() примет такой вид:
Code
void main() { setlocale(0,""); int a, a2; a=GetDigit(); // ввод первого числа a2=GetDigit(); // ввод второго числа cout<<"Сума двух чисел "<< sum(a,a2); // вызов функции cin.get(); cin.get(); }
Здесь переменные a и a2 получают корректное значение, возвращенное нашей функцией.
ЗАДАЧКА. До сих пор я не давал вам домашних заданий. Пора это менять. Итак, небольшая задачка. Измените приведенный выше пример так, чтобы переменная а2 оказалась ненужной.
Ну а теперь обещанный мной более продвинутый вариант функции GetDigit().
Code
int GetDigit() { int d; cout<<"Введите число "; while (true) { cin >> d; if ((cin.peek() == '\n')) break; else { cout << "Повторите ввод (ожидается число ) " << "\n"; cin.clear(); while (cin.get() != '\n') {
} } } return d; }
Я не буду вам объяснять как она работает. Вы должны попытаться разобраться в ней сами. В этом вам поможет встроенная в студию справка, или если у вас нелады с английским то Интернет. Основное внимание в этом примере следует уделить новым функциям: cin.peek() cin.clear() cin.get() Правда с последней вы уже знакомы, но вот что она делает на самом деле вряд ли знаете.
Ах да. Я же на протяжении стольких уроков так и не рассказал, как пользоваться справкой . Виноват. Исправляюсь. Хотя там и рассказывать особо нечего. Как и во всех других Windows приложениях, справка в студии вызывается нажатием клавиши F1. При этом справка здесь умная. Во вновь открывшемся окне выдается информация об элементе, который в данный момент является активным. Чтобы получить информацию о неизвестной вам стандартной функции языка, достаточно поместить на нее курсор и нажать вышеупомянутую клавишу.
$IMAGE1$
И оп… Мы получаем всю возможную информацию о данной функции, а также пример ее использования, правда, чаще всего на английском.
$IMAGE2$
Кроме получения информации по функциям в справке содержится еще много чего. Например, можно узнать побольше о какой-нибудь возникшей ошибке. Для этого в списке ошибок выделяем интересующую:
$IMAGE3$
И нажимаем F1. В появившемся окне можно получить информацию не только о природе ошибки, но и подробно, с примерами, узнать причины ее возникновения, а также способы устранения. Кстати если у вас русская студия, то информацию по ошибкам вы получите на родном языке. Эту часть они уже успели перевести.
Порадовал я вас немного рисунками, пора возвращаться к основной теме. Ну вот вы и научились создавать и использовать функции. Теперь сделаю небольшое дополнение. Функции объявлять не обязательно. Вместо объявления функции можно сразу написать ее реализацию. Вот приводимый ранее пример без объявления.
Code
#include <iostream> using namespace std;
int sum(int a, int b) //определение(реализация) функции вместо ее обьявления. { return a + b; }
void main() { setlocale(0,"");
. . . cin.get(); cin.get(); }
Возникает вопрос, зачем же тогда оно нужно. Это вы сможете понять со следующего урока.