Лекция 5. Знакомьтесь - Функции.
Здравствуйте.
На третьей лекции вы познакомились с циклами. То есть со способами многократного повторения одного определенного блока кода. Давайте представим себе такую ситуацию. Допустим нам необходимо повторить этот блок кода несколько раз но не в одном месте, а в различных частях программы. Конечно, можно просто написать его несколько раз, более того можно просто этот код скопировать. Но есть способ эффективнее и проще – использовать функции.
Урок 1. Функции
Когда программы становятся очень большими в коде реально можно потеряться. Единственный способ избежать подобных неприятностей, это разделить программу на несколько частей, и работать с ними по отдельности. Этими частями и будут функции. Такой способ программирования называют Процедурным программированием.(Процедура и функция это одно и тоже).
Что же такое функция, по-простому это отдельный блок кода, имеющий определенное назначение, то есть выполняющий какую-то задуманную программистом работу.
Вообще-то с функциями мы уже встречались. Вспомните, когда мы работали с генератором случайных чисел, мы получали системное время с помощью функции time(), устанавливали начальное значение генератора с помощью функции srand(), ну а само число получали с помощью rand().
Использование функции можно разделить на три этапа:
1. Объявление функции
2. Определение функции
3. Вызов.
Объявление функции
Объявление сообщает компилятору о существовании некой функции. Вот синтаксис:
Code
Тип_возвращаемого_значения имя_функции(список агрументов);
Здесь:
Тип возвращаемого значения – это тип переменной, которую возвращает функция по завершению своей работы. Как я уже сказал, у каждой функции есть свое назначение, она выполняет определенную работу. Возьмем простой пример – функция умеет суммировать два целых числа. Результат этого сложения и будет возвращен в виде целого числа.
Имя функции – это и есть имя. Ведь к функции как то нужно обращаться. К именам функций применимы те же требования что и к именам переменной. Помните главное - имя должно быть понятным.
Список аргументов(еще называют параметры функции) - это записанные в скобках передаваемые функции значения, попросту говоря это список переменных над которыми эта функция будет совершать определенные действия. Объявляются эти аргументы так же как и обычные переменные, и дальнейшем используются внутри функции. Несколько аргументов отделяются друг от друга запятыми. Количество аргументов не ограничено, но им может и не быть вовсе.
И обращаю ваше внимание, что завершает объявление уже знакомые нам точка с запятой.
Пример объявления функций:
Подобную запись еще называют прототипом функции. Здесь у нас объявлена функция которая не возвращает никакого значения( тип void – пустой тип) и не принимающая ни одного аргумента. Судя из названия, функция умеет что-то там печатать.
Code
int sum(int a, int b);
Это уже немного описанный пример. Функция имеет два аргумента типа 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
if(условие)
{
return значение;
}
else
{
return значение2;
}
Здесь возвращенное значение зависит от заданного условия.
Вызов функции.
Чтобы воспользоваться функцией, необходимо произвести ее вызов в нужном месте программы. Для вызова используется следующий синтаксис:
Code
имя_функции (значение1, значение2, …)
Здесь количество и тип значений должны соответствовать количеству и типу аргументов, объявленных в прототипе вызываемой функции. То есть, для объявленной ранее функции
Code
int sum(int a, int b);
вызов будет таким:
Даже если функция не имеет аргументов, круглые скобки при ее вызове писать обязательно.
Кроме чисел функции также можно передавать переменные соответствующего типа:
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; // возврат числа
здесь с – имя массива, по совместительству являющееся также и указателем. Эту запись можно сократить, ведь абсолютно незачем использовать лишнюю переменную
Code
return atoi(c); // возврат числа
Здесь напрямую возвращается результат работы функции atoi©. То есть, можно сказать, что после выполнения работы функция замещается значением возвращаемого ею типа. Поэтому ее и можно использовать в тексте программы непосредственно как значение:
Code
b = sum(5,10)* sum(5,3);
аналогом этой записи будет
то есть:
Надеюсь это уяснили. Теперь внесем в нашу программу изменения, задействуем созданную нами функцию 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. При этом справка здесь умная. Во вновь открывшемся окне выдается информация об элементе, который в данный момент является активным. Чтобы получить информацию о неизвестной вам стандартной функции языка, достаточно поместить на нее курсор и нажать вышеупомянутую клавишу.
И оп…
Мы получаем всю возможную информацию о данной функции, а также пример ее использования, правда, чаще всего на английском.
Кроме получения информации по функциям в справке содержится еще много чего. Например, можно узнать побольше о какой-нибудь возникшей ошибке. Для этого в списке ошибок выделяем интересующую:
И нажимаем F1. В появившемся окне можно получить информацию не только о природе ошибки, но и подробно, с примерами, узнать причины ее возникновения, а также способы устранения. Кстати если у вас русская студия, то информацию по ошибкам вы получите на родном языке. Эту часть они уже успели перевести.
Порадовал я вас немного рисунками, пора возвращаться к основной теме.
Ну вот вы и научились создавать и использовать функции. Теперь сделаю небольшое дополнение. Функции объявлять не обязательно. Вместо объявления функции можно сразу написать ее реализацию. Вот приводимый ранее пример без объявления.
Code
#include <iostream>
using namespace std;
int sum(int a, int b) //определение(реализация) функции вместо ее обьявления.
{
return a + b;
}
void main()
{
setlocale(0,"");
. . .
cin.get();
cin.get();
}
Возникает вопрос, зачем же тогда оно нужно. Это вы сможете понять со следующего урока.