refs to code in section 06, going on with functions in main

This commit is contained in:
Ivan I. Ovchinnikov 2021-08-23 16:53:38 +03:00
parent aa94b1f738
commit 9586a31f27
3 changed files with 70 additions and 78 deletions

Binary file not shown.

145
main.tex
View File

@ -21,87 +21,76 @@
\import{sections/}{05-conditions}
% 06 cycles
\import{sections/}{06-cycles}
% \section{Функции}
% Функция - это такая обособленная часть кода, которую можно выполнять любое количество раз. У функций обязательно в таком порядке должны быть описаны: тип возвращаемого значения, название, аргументы и так называемое тело, то есть собственно исполняемый код. Рассмотрим более детально функцию \code{int main (int argc, char *argv[])}: \code{int} - это \textit{тип возвращаемого значения}, то есть на том месте, откуда будет вызвана эта функция, в результате её работы по выполнении оператора \code{return;}, появится некое целое число. Возвращаемые значения могут быть любых типов. В случае же когда функция не должна возвращать результат своей работы, или никакого возвращаемого результата не предполагается, указывается ключевое слово \code{void} (англ. - пустота). То есть на месте вызова функции в результате её выполнения ничего не появится. Оператор \code{return;} обязателен для не-void функций, а в \code{void} функциях может присутствовать или нет, но никогда не содержит возвращаемого значения. \code{main} - это \textit{название функции}. Функция именно с таким названием, написанным с маленькой буквы, всегда является точкой входа в программу (\hyperref[text:main]{\ref{text:main}}). Операционная система ищет именно эту функцию, когда получает команду на выполнение программы.
% \frm{Названия функций в рамках одной программы не должны повторяться и не должны начинаться с цифр или спецсимволов, также, как и названия переменных (\hyperref[text:naming]{\ref{text:naming}}) никаких других ограничений на название функций не накладывается.}
% Конструкция в круглых скобках \code{(int argc, char *argv[])} - это \textit{аргументы функции}. Аргументы функции - это такие переменные, которые создаются при вызове функции и существуют только внутри неё. С их помощью можно передать в функцию какие-то параметры и исходные данные для работы. Аргументы пишутся в круглых скобках сразу после названия функции. В случае если функция не принимает аргументов необходимо поставить после названия пустые круглые скобки. Весь код, содержащийся в фигурных скобках после аргументов функции называется \textit{телом функции}. Это те операторы и команды, которые будут последовательно выполнены при вызове функции. В теле функции мы можем \textbf{вызывать} другие функции, но никогда не можем создавать в теле функции другие функции. Никаких других ограничений на написание тела функции язык не накладывает.
% \begin{verbatim}
% ТипВозвращаемогоЗначения Имя (СписокАргументов)
% {
% ТелоФункции
% return ВозвращаемоеЗначение;
% }
% \end{verbatim}
% \begin{lstlisting}[language=C,style=CCodeStyle]
% void somefunction() { // <-- this is a function
% // some useful things
% }
\section{Функции}
\subsection{Понятие функции, параметры и аргументы}
Функция - это такая обособленная часть кода, которую можно выполнять любое количество раз. У функций обязательно в таком порядке должны быть описаны: тип возвращаемого значения, название, параметры и так называемое тело, то есть собственно исполняемый код. Рассмотрим более детально функцию \code{int main (int argc, char *argv[])}: \code{int} - это \textit{тип возвращаемого значения}, то есть на том месте, откуда будет вызвана эта функция, в результате её работы по выполнении оператора \code{return;}, появится некое целое число. Возвращаемые значения могут быть любых типов. В случае же когда функция не должна возвращать результат своей работы, или никакого возвращаемого результата не предполагается, указывается ключевое слово \code{void} (англ. - пустота). То есть на месте вызова функции в результате её выполнения ничего не появится. Оператор \code{return;} обязателен для не-void функций, а в \code{void} функциях может присутствовать или нет, но никогда не содержит возвращаемого значения. \code{main} - это \textit{название функции}. Функция именно с таким названием, написанным с маленькой буквы, всегда является точкой входа в программу (\hyperref[text:main]{\ref{text:main}}). Операционная система ищет именно эту функцию, когда получает команду на выполнение программы.
\frm{Названия функций в рамках одной программы не должны повторяться и не должны начинаться с цифр или спецсимволов, также, как и названия переменных (\hyperref[text:naming]{\ref{text:naming}}) никаких других ограничений на название функций не накладывается.}
Конструкция в круглых скобках \code{(int argc, char *argv[])} - это \textit{параметры функции}. Параметры функции - это такие переменные, которые создаются при вызове функции и существуют только внутри неё. С их помощью можно передать в функцию какие-то аргументы и исходные данные для работы. Параметры пишутся в круглых скобках сразу после названия функции. В случае если функция не принимает параметров необходимо поставить после названия пустые круглые скобки. Весь код, содержащийся в фигурных скобках после параметров функции называется \textit{телом функции}. Это те операторы и команды, которые будут последовательно выполнены при вызове функции. В теле функции мы можем \textbf{вызывать} другие функции, но \textbf{никогда не можем создавать в теле функции другие функции}. Никаких других ограничений на написание тела функции язык не накладывает.
\begin{verbatim}
ТипВозвращаемогоЗначения Имя (СписокАргументов)
{
ТелоФункции
return ВозвращаемоеЗначение;
}
\end{verbatim}
Далее приведём небольшой пример, который призван продемонстрировать, как выглядит простейшее \textit{объявление} и \textit{описпание} функций (function declaration and definition).
\begin{lstlisting}[language=C,style=CCodeStyle]
void somefunction() { // <-- this is a function
printf("some function\n");
// some useful things
}
int anotherFunction() {
printf("another function\n");
// more useful things happened
return 10;
}
% int main (int argc, const char* argv[]) {
% // more useful things
% somefunction(); // <-- this is invocation
% return 0;
% }
% \end{lstlisting}
int main (int argc, const char* argv[]) {
printf("main function\n");
// more useful things
somefunction(); // <-- this is invocation
int x = anotherFunction();
printf("x = %d\n", 10);
return 0;
}
\end{lstlisting}
Так, на тринадцатой строке кода выше мы видим, что \textbf{вернувшееся} из функции, объявленной на пятой строке целое число \code{10} будет присвоено переменной \code{x}.
\begin{verbatim}
$ ./program
main function
some function
another function
x = 10
$
\end{verbatim}
Функции принято разделять на проверяющие, считающие и выводящие, и каждая из вышеописанных функций не должна нести дополнительной нагрузки. То есть, функция не должна знать откуда в программе появились её параметры, и где будет использован результат её работы. То есть сам язык таких ограничений не накладывает, но такой подход к написанию функций делает их значительно более гибкими и даёт им возможность быть переиспользованными. Без применения такого подхода было бы невозможно писать абстрактные библиотеки и фреймворки.
\frm{\textbf{Параметры функции} - это те переменные, которые указываются в круглых скобках при определении или описании функции. Параметры функции существуют как локальные переменные в кодовом блоке тела функции.\textbf{Аргументы функции} - это те значения переменных или литералов, которые указываются в круглых скобках при выхове функции.}
Для примера опишем функцию, суммирующую два числа. Для простоты, в качестве аргументов она будет принимать целые числа и возвращать целочисленный результат. Обратите внимание что функция не <<знает>> откуда взялись эти числа, мы можем их прочитать из консоли, можем задать в виде констант или получить в результате работы какой-то другой функции. Внутри функции \code{int main (int argc, char *argv[])} мы вызываем нашу функцию \code{sum(int x, int y)} суммирующую два числа и передаём в качестве аргументов эти числа.
\begin{lstlisting}[language=C,style=CCodeStyle]
int sum(int x, int y) {
int result = x + y;
return result;
}
% Функции принято разделять на проверяющие, считающие и выводящие, и каждая из вышеописанных функций не должна нести дополнительной нагрузки. То есть, функция не должна знать откуда в программе появились её аргументы, и где будет использован результат её работы. Для примера опишем функцию суммирующую два числа. в качестве аргументов она будет принимать целые числа и возвращать целочисленный результат. Обратите внимание что функция не знает откуда взялись эти числа, мы можем их считать с консоли, можем задать в виде констант или получить в результате работы какой то другой функции.
% Внутри функции main мы вызываем нашу функцию sum суммирующую два числа и передаем в качестве аргументов эти числа.
% int sum(int x, int y) {
% int result = x + y;
% return result;
% }
% int main (int argc, const char* argv[]) {
% sum(50, 60);
% return 0;
% }
% Обратите внимание, что в качестве аргументов мы можем передавать константные значения, а также переменные. Значения переменных мы можем получить например из консоли, либо в результате выполнения какой-нибудь другой функции.
% int main (int argc, const char* argv[]) {
% int a;
% scanf(“%d, &a”);
% sum(50, a);
% return 0;
% }
% Как уже было сказано - аргументы - это переменные, которые хранят в себе некоторые параметры вызова функции. Аргументы позволяют использовать одни и те же функции с разными исходными данными. Приглядимся повнимательнее к хорошо знакомой нам функции printf(). Строка, которую мы пишем в круглых скобках в двойных кавычках это и есть аргумент функции. То есть мы знаем, что функция умеет выводить на экран строки. Как именно - нам нет дела, а какие именно строки - мы указываем в качестве аргумента.
% Функция printf() примечательна еще и тем, что она может принимать в себя нефиксированное количество аргументов. Описание работы таких функций, а также их написание выходит далеко за пределы основ языка, нам важно помнить что мы можем это использовать. В аргументе функции printf() мы можем написать заполнитель соответствующего типа и вызвать нашу функцию sum.
% printf(“HelloWorld!”);
% printf(“%d”, sum(50, a));
% Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функций программу проверки простоты числа.
% Для этого опишем функцию которая возвращает целое число, назовем ее isPrime, в качестве аргумента она принимает целое число, назовем его number. Найдем из предыдущих занятий программу определения простоты числа и скопируем в тело функции.
int main (int argc, const char* argv[]) {
int a;
scanf("%d", &a);
int x = sum(50, a);
printf("x = %d\n", 10);
return 0;
}
\end{lstlisting}
Обратите внимание, что в качестве аргументов мы можем передавать константные значения, а также переменные. Значения переменных мы можем получить например из консоли, либо в результате выполнения какой-нибудь другой функции.
\begin{verbatim}
$ ./program
x = 110
$
\end{verbatim}
Как уже было сказано, параметры - это переменные, которые хранят в себе некоторые начальные значения вызова функции. Параметризация позволяет использовать одни и те же функции с разными исходными данными. Приглядимся повнимательнее к хорошо знакомой нам функции \code{printf();}. Строка, которую мы пишем в круглых скобках в двойных кавычках - это аргумент функции. То есть мы знаем, что функция умеет выводить на экран строки, как именно - нам нет дела, а какие именно строки - мы указываем в качестве аргумента. Функция \code{printf();} примечательна еще и тем, что она может принимать в себя нефиксированное количество аргументов. Описание работы таких функций, а также их написание выходит далеко за пределы основ языка, нам важно помнить что мы можем это использовать. В аргументе функции \code{printf()} мы можем написать заполнитель соответствующего типа и, например, вызвать нашу функцию \code{sum}.
\subsection{Оформление функций. Рефакторинг кода}
Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функции программу проверки простоты числа. Для этого опишем функцию которая возвращает целое число, назовем ее \code{isPrime()}, в качестве параметра она будет принимать целое число, назовем его \code{number}. Найдем в предыдущих разделах (стр. \hyperref[code:isPrime]{\pageref{code:isPrime}}) программу определения простоты числа и скопируем в тело функции.
% Внесем небольшие правки, уберем вывод т.к. это проверяющая функция, вывод оставим для основной части. И допишем если делителей 2 то число простое, возвращаем 1. Если же делителей больше число не простое, возвращаем 0.
% Такой вывод можно записать и другим способом, return (d == 2) это выражение в случае истины вернет 1 в случае лжи 0.
% Можно воспользоваться тернарным оператором, т.е. написать

View File

@ -144,6 +144,7 @@ printf("%d powered by %d is %d \n", base, significative, result);
\end{lstlisting}
Конечно, мы можем спросить у пользователя какое число, и в какую степень он хочет возвести, для этого применим уже привычные нам конструкции. Так, весь код программы будет иметь следующий вид:
\lstinputlisting[language=C,style=CCodeStyle]{../sources/power.c}
\label{code:power}
Запустим нашу программу, введем для базы значение два, для показателя десять. Убедимся, что наша программа работает корректно, $2^{10}=1024$.
\begin{verbatim}
$ ./program
@ -185,6 +186,7 @@ if (dividers == 3)
\end{lstlisting}
Если количество целочисленных делителей не изменилось, то мы прекратим текущую итерацию цикла при помощи ключевого слова \code{continue}. Как мы знаем, оператор \code{continue} передаст управление в логическую конструкцию цикла, заставив программу проигнорировать все дальнейшие инструкции в рамках текущей итерации. Если количество целочисленных делителей достигнет трёх, что будет означать нецелесообразность дальнейших вычислений, мы разорвем цикл при помощи ключевого слова \code{break}. И полный получившийся код приложения будет такой:
\lstinputlisting[language=C,style=CCodeStyle]{../sources/prime.c}
\label{code:isPrime}
Естественно, повторимся, этот код можно оптимизировать по множеству направлений, как минимум, сократив как количество проверок, так и границы проверок (нет смысла проверять числа больше, чем $\sqrt{number}$). Дополнительно можно не проверять чётные числа, например.
\begin{verbatim}
$ ./program
@ -253,6 +255,7 @@ default:
\frm{Важно помнить, что в случае отсутствия внутри \code{case} оператора \code{break;}, программа будет выполнять последующие кейсы, пока не найдёт \code{break;} или пока не закончится конструкция \code{switch()\{\}}, то есть пока не встретится её закрывающая фигурная скобка.}
В кейсах мы опишем присваивание результата в переменную \code{result}, а после выхода из \code{switch()\{\}} - вывод результата в консоль. Кейсом по умолчанию будет вывод пользователю сообщения о невозможности распознать оператор. Так получается, что даже если мы ввели неизвестный оператор, программа попытается вывести в консоль результат, что неприемлемо. Поэтому кейс по умолчанию должен содержать также и оператор \code{return 1;} вынуждающий программу экстренно завершиться с кодом ошибки \code{1}. Получится такой код:
\lstinputlisting[language=C,style=CCodeStyle]{../sources/calculator.c}
\label{code:calculator}
Запустив описанный нами калькулятор, убедимся что все работает. Сымитируем нерадивого пользователя и несколько раз попробуем ввести при использовании четвёртого оператора цифру ноль, программа естественно не даст нам этого сделать.
\begin{verbatim}
$ ./program