forked from ivan-igorevich/basic-c
reformatted for figures and added text to fill page
This commit is contained in:
parent
bb68a8cd2d
commit
81a9193105
BIN
build/main.pdf
BIN
build/main.pdf
Binary file not shown.
|
@ -3,6 +3,7 @@
|
||||||
\usepackage{listings}
|
\usepackage{listings}
|
||||||
\usepackage{xcolor}
|
\usepackage{xcolor}
|
||||||
\usepackage{bookmark}
|
\usepackage{bookmark}
|
||||||
|
\usepackage{multicol}
|
||||||
\usepackage{setspace}
|
\usepackage{setspace}
|
||||||
\usepackage{titlesec}
|
\usepackage{titlesec}
|
||||||
\usepackage{indentfirst}
|
\usepackage{indentfirst}
|
||||||
|
|
37
main.tex
37
main.tex
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
\section{Функции}
|
\section{Функции}
|
||||||
\subsection{Понятие функции, параметры и аргументы}
|
\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}}). Операционная система ищет именно эту функцию, когда получает команду на выполнение программы.
|
Функция - это такая обособленная часть кода, которую можно выполнять любое количество раз. У функций обязательно в таком порядке должны быть описаны: тип возвращаемого значения, название, параметры и так называемое тело, т есть собственно исполняемый код. Рассмотрим более детально функцию \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}}) никаких других ограничений на название функций не накладывается.}
|
\frm{Названия функций в рамках одной программы не должны повторяться и не должны начинаться с цифр или спецсимволов, также, как и названия переменных (\hyperref[text:naming]{\ref{text:naming}}) никаких других ограничений на название функций не накладывается.}
|
||||||
Конструкция в круглых скобках \code{(int argc, char *argv[])} - это \textit{параметры функции}. Параметры функции - это такие переменные, которые создаются при вызове функции и существуют только внутри неё. С их помощью можно передать в функцию какие-то аргументы и исходные данные для работы. Параметры пишутся в круглых скобках сразу после названия функции. В случае если функция не принимает параметров необходимо поставить после названия пустые круглые скобки. Весь код, содержащийся в фигурных скобках после параметров функции называется \textit{телом функции}. Это те операторы и команды, которые будут последовательно выполнены при вызове функции. В теле функции мы можем \textbf{вызывать} другие функции, но \textbf{никогда не можем создавать в теле функции другие функции}. Никаких других ограничений на написание тела функции язык не накладывает.
|
Конструкция в круглых скобках \code{(int argc, char *argv[])} - это \textit{параметры функции}. Параметры функции - это такие переменные, которые создаются при вызове функции и существуют только внутри неё. С их помощью можно передать в функцию какие-то аргументы и исходные данные для работы. Параметры пишутся в круглых скобках сразу после названия функции. В случае если функция не принимает параметров необходимо поставить после названия пустые круглые скобки. Весь код, содержащийся в фигурных скобках после параметров функции называется \textit{телом функции}. Это те операторы и команды, которые будут последовательно выполнены при вызове функции. В теле функции мы можем \textbf{вызывать} другие функции, но \textbf{никогда не можем создавать в теле функции другие функции}. Никаких других ограничений на написание тела функции язык не накладывает.
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
|
@ -92,25 +92,24 @@ $
|
||||||
\subsection{Оформление функций. Понятие рефакторинга}
|
\subsection{Оформление функций. Понятие рефакторинга}
|
||||||
Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функции программу проверки простоты числа. Для этого опишем функцию которая возвращает целое число, назовем ее \code{isPrime()}, в качестве параметра она будет принимать целое число, назовем его \code{number}. Найдем в предыдущих разделах (стр. \hyperref[code:isPrime]{\pageref{code:isPrime}}) программу определения простоты числа и скопируем в тело функции. Внесем небольшие правки, уберем вывод так как это будет, можно сказать, классическая проверяющая функция, вывод оставим для функции \code{int main (int argc, char *argv[])}, пусть о наличии у нас терминала <<знает>> только она.
|
Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функции программу проверки простоты числа. Для этого опишем функцию которая возвращает целое число, назовем ее \code{isPrime()}, в качестве параметра она будет принимать целое число, назовем его \code{number}. Найдем в предыдущих разделах (стр. \hyperref[code:isPrime]{\pageref{code:isPrime}}) программу определения простоты числа и скопируем в тело функции. Внесем небольшие правки, уберем вывод так как это будет, можно сказать, классическая проверяющая функция, вывод оставим для функции \code{int main (int argc, char *argv[])}, пусть о наличии у нас терминала <<знает>> только она.
|
||||||
\frm{Такой процесс, перенос участков кода между функциями, выделение участков кода в функции, синтаксические, стилистические и другие улучшения, называетя \textbf{рефакторингом}. Обычно, рефакторингом занимаются сами разработчики в свободное от основной деятельности времени, в периоды код ревью или по необходимости улучшить читаемость/повторяемость собственного кода.}
|
\frm{Такой процесс, перенос участков кода между функциями, выделение участков кода в функции, синтаксические, стилистические и другие улучшения, называетя \textbf{рефакторингом}. Обычно, рефакторингом занимаются сами разработчики в свободное от основной деятельности времени, в периоды код ревью или по необходимости улучшить читаемость/повторяемость собственного кода.}
|
||||||
Следовательно, допишем условия: если делителей два, то число простое, возвращаем \code{ИСТИНУ}, то есть любое ненулевое значение, в нашем примере - единицу. Если же делителей больше – число не простое, возвращаем \code{ЛОЖЬ}, в нашем случае, это ноль. Такой вывод можно записать и другим способом, \code{return (d == 2)} – это выражение в случае истины вернет единицу в случае лжи ноль. Или можно воспользоваться тернарным оператором, то есть, написать \code{return (d == 2) ? 1 : 0}: если условие в скобках истинно вернется единица, ложно – ноль. Также важно, что выйти из функции мы можем на любом этапе ее выполнения, например если делителей уже три, то нам нужно не завершать цикл, а вернуть \code{ЛОЖЬ} из функции.
|
Следовательно, допишем условия: если делителей два, то число простое, возвращаем \code{ИСТИНУ}, то есть любое ненулевое значение, в нашем примере - единицу. Если же делителей больше – число не простое, возвращаем \code{ЛОЖЬ}, в нашем случае, это ноль. Такой вывод можно записать и другим способом, \code{return (dividers == 2)} – это выражение в случае истины вернет единицу в случае лжи ноль. Или можно воспользоваться тернарным оператором, то есть, написать \code{return (dividers == 2) ? 1 : 0}: если условие в скобках истинно вернется единица, ложно – ноль. Также важно, что выйти из функции мы можем на любом этапе ее выполнения, например если делителей уже три, то нам нужно не завершать цикл, а вернуть \code{ЛОЖЬ} из функции.
|
||||||
\begin{multicols}{2}
|
\begin{multicols}{2}
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
|
||||||
int isPrime(int number){
|
int isPrime(int number){
|
||||||
int d = 0, i = 1;
|
int dividers = 0, i = 1;
|
||||||
|
|
||||||
|
|
||||||
while(i <= number){
|
while(i <= number){
|
||||||
if(number % i++ ==0)
|
if(number % i++ ==0)
|
||||||
d++;
|
dividers++;
|
||||||
else
|
else
|
||||||
continue;
|
continue;
|
||||||
if (d == 3) return 0;
|
|
||||||
|
if (dividers == 3)
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
//if (d == 2) return 1;
|
return (dividers == 2)
|
||||||
//else return 0;
|
|
||||||
|
|
||||||
//return (d == 2) ? 1 : 0;
|
|
||||||
|
|
||||||
return (d == 2)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\columnbreak
|
\columnbreak
|
||||||
|
@ -125,14 +124,14 @@ int main(int argc, char *argv[]) {
|
||||||
dividers++;
|
dividers++;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
if (dividers == 3)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (dividers == 3)
|
printf("Number %d is%s prime",
|
||||||
break;
|
number,
|
||||||
}
|
(dividers == 2) ? "" : " not"
|
||||||
printf("Number %d is%s prime",
|
);
|
||||||
number,
|
|
||||||
(dividers == 2) ? "" : " not"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
\end{multicols}
|
\end{multicols}
|
||||||
|
|
|
@ -10,19 +10,28 @@
|
||||||
Применим этот общий алгоритм для написания первой программы. Предполагается, что на данный момент у Вас установлена либо среда разработки, либо текстовый редактор и компилятор по отдельности, чтобы иметь возможность повторить этот и все последующие примеры самостоятельно.
|
Применим этот общий алгоритм для написания первой программы. Предполагается, что на данный момент у Вас установлена либо среда разработки, либо текстовый редактор и компилятор по отдельности, чтобы иметь возможность повторить этот и все последующие примеры самостоятельно.
|
||||||
\paragraph{Первая программа, файл \code{program.c}}
|
\paragraph{Первая программа, файл \code{program.c}}
|
||||||
Для написания программы, откроем выбранный текстовый редактор или среду программирования, и напишем следующие строки (важно отличать заглавные и строчные буквы, то есть, например \code{Int} и \code{int} - это разные слова, и первое, написанное с заглавной буквы, будет не понято компилятором):
|
Для написания программы, откроем выбранный текстовый редактор или среду программирования, и напишем следующие строки (важно отличать заглавные и строчные буквы, то есть, например \code{Int} и \code{int} - это разные слова, и первое, написанное с заглавной буквы, будет не понято компилятором):
|
||||||
\lstinputlisting[language=C,style=CCodeStyle]{../sources/helloworld.c}
|
|
||||||
\label{code:firstprogram}
|
\begin{figure}[h!]
|
||||||
|
\lstinputlisting[language=C,style=CCodeStyle]{../sources/helloworld.c}
|
||||||
|
\label{code:firstprogram}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Запуск компиляции и исполнение программы}
|
\paragraph{Запуск компиляции и исполнение программы}
|
||||||
В зависимости от выбранного инструментария и ОС процесс компиляции (трансляции) и запуска программы на исполнение может незначительно отличаться, далее будут приведены несколько вариантов. Естественно, что не нужно выполнять их все, а следует выбрать один, который сработает именно с Вашим набором инструментов. Трансляция (компиляция):
|
В зависимости от выбранного инструментария и ОС процесс компиляции (трансляции) и запуска программы на исполнение может незначительно отличаться, далее будут приведены несколько вариантов. Естественно, что не нужно выполнять их все, а следует выбрать один, который сработает именно с Вашим набором инструментов. Трансляция (компиляция):
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
clang -o program program.c
|
clang -o program program.c
|
||||||
(или) gcc -o program program.c
|
(или) gcc -o program program.c
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Запуск будет отличаться только для Windows (символами доллара и угловой скобки обозначены приглашения unix-терминала и командной строки windows, соответственно):
|
Запуск будет отличаться только для Windows (символами доллара и угловой скобки обозначены приглашения unix-терминала и командной строки windows, соответственно):
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
non-windows $ ./program
|
non-windows $ ./program
|
||||||
windows > .\program.exe
|
windows > .\program.exe
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Далее в тексте в целях демонстрации будет использоваться запуск в стиле non-windows, а также, будет опускаться демонстрация этапа компиляции (кроме случаев, когда сам процесс компиляции отличается).
|
Далее в тексте в целях демонстрации будет использоваться запуск в стиле non-windows, а также, будет опускаться демонстрация этапа компиляции (кроме случаев, когда сам процесс компиляции отличается).
|
||||||
\subsection{Шаблон программы}
|
\subsection{Шаблон программы}
|
||||||
Как и программы на любом другом языке, программы на языке С имеют ряд обязательных элементов, также называемых шаблоном программы. Рассмотрим эти элементы подробнее на примере только что написанной первой программы (\hyperref[code:firstprogram]{\ref{code:firstprogram}}). Некоторые, незначительные аспекты, будут рассмотрены сразу и полностью, комментарии, например, но некоторые будут рассмотрены поверхностно, поскольку являются масштабными, сложными для понимания фундаментальными механизмами языка, которым будут посвящены целые специальные разделы.
|
Как и программы на любом другом языке, программы на языке С имеют ряд обязательных элементов, также называемых шаблоном программы. Рассмотрим эти элементы подробнее на примере только что написанной первой программы (\hyperref[code:firstprogram]{\ref{code:firstprogram}}). Некоторые, незначительные аспекты, будут рассмотрены сразу и полностью, комментарии, например, но некоторые будут рассмотрены поверхностно, поскольку являются масштабными, сложными для понимания фундаментальными механизмами языка, которым будут посвящены целые специальные разделы.
|
||||||
|
|
|
@ -19,14 +19,19 @@
|
||||||
\item \code{\char`\\unnnn} - произвольное Юникод-значение.
|
\item \code{\char`\\unnnn} - произвольное Юникод-значение.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
Чтобы убедится что это правильно работает выведем еще одну строку (в дополнение к коду со страницы \hyperref[code:firstprogram]{\pageref{code:firstprogram}}) с надписью <<Это новая строка>> на следующую строку нашей консоли. Также добавим к ещё одной строке символ табуляции чтобы увидеть как он работает. И сразу рассмотрим экранированную последовательность \code{\char`\\\char`\\} она делает ни что иное как добавляет символ обратного слэша в наш текст. Аналогичным образом работают и другие символьные экранирования. Это нужно, чтобы компилятор мог отличить, например, символ двойных кавычек в строке, написанной программистом, от символа двойных кавычек, завершающего строку в коде программы. Обратите внимание что не поставив в конец строки последовательность \code{\char`\\n} мы заставим компилятор постоянно писать текст на одной строке, не переходя на новую. И наконец \code{\char`\\0} сообщает компилятору что строка закончилась. Даже если у нас есть еще какие-то символы до закрывающих кавычек компилятор их просто проигнорирует. Добавим в код программы ещё пару строк, таким образом тело функции \code{main} должно принять следующий вид:
|
Чтобы убедится что это правильно работает выведем еще одну строку (в дополнение к коду со страницы \hyperref[code:firstprogram]{\pageref{code:firstprogram}}) с надписью <<Это новая строка>> на следующую строку нашей консоли. Также добавим к ещё одной строке символ табуляции чтобы увидеть как он работает. И сразу рассмотрим экранированную последовательность \code{\char`\\\char`\\} она делает ни что иное как добавляет символ обратного слэша в наш текст. Аналогичным образом работают и другие символьные экранирования. Это нужно, чтобы компилятор мог отличить, например, символ двойных кавычек в строке, написанной программистом, от символа двойных кавычек, завершающего строку в коде программы. Обратите внимание что не поставив в конец строки последовательность \code{\char`\\n} мы заставим компилятор постоянно писать текст на одной строке, не переходя на новую. И наконец \code{\char`\\0} сообщает компилятору что строка закончилась. Даже если у нас есть еще какие-то символы до закрывающих кавычек компилятор их просто проигнорирует. Добавим в код программы ещё пару строк, таким образом тело функции \code{main} должно принять следующий вид:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
printf("Hello, world!\n");
|
\begin{figure}[h!]
|
||||||
printf("This is a new row");
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("This is \"\t\"tab\n");
|
printf("Hello, world!\n");
|
||||||
printf("This is \\ symbol\n");
|
printf("This is a new row");
|
||||||
printf("This is a terminant \0 it ends a string");
|
printf("This is \"\t\"tab\n");
|
||||||
\end{lstlisting}
|
printf("This is \\ symbol\n");
|
||||||
|
printf("This is a terminant \0 it ends a string");
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Запустив программу мы можем убедиться, что всё работает так, как мы описали в тексте: сначала идёт приветствие миру, на новой строке сообщение о том, что это новая строка, далее на той же строке (ведь мы не переходили на новую) демонстрация пробела и символа табуляции. На последних двух строках происходит демонстрация обратного слэша и терминанта, видно, что остаток строки не был выведен на экран.
|
Запустив программу мы можем убедиться, что всё работает так, как мы описали в тексте: сначала идёт приветствие миру, на новой строке сообщение о том, что это новая строка, далее на той же строке (ведь мы не переходили на новую) демонстрация пробела и символа табуляции. На последних двух строках происходит демонстрация обратного слэша и терминанта, видно, что остаток строки не был выведен на экран.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Hello, World!
|
Hello, World!
|
||||||
|
@ -35,6 +40,7 @@ This is \ symbol
|
||||||
This is terminant
|
This is terminant
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Заполнитель} это также специальная последовательность, но она говорит компилятору, что на место этой последовательности необходимо вставить некий аргумент, который будет передан после строки, через запятую, при вызове данной функции \code{printf();}. Заполнитель начинается со знака процента и обозначает тип вставляемой переменной.
|
\paragraph{Заполнитель} это также специальная последовательность, но она говорит компилятору, что на место этой последовательности необходимо вставить некий аргумент, который будет передан после строки, через запятую, при вызове данной функции \code{printf();}. Заполнитель начинается со знака процента и обозначает тип вставляемой переменной.
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \code{\%\%} - символ процента;
|
\item \code{\%\%} - символ процента;
|
||||||
|
@ -48,15 +54,20 @@ $
|
||||||
\item \code{\%x (\%X)} - беззнаковое целое в шестнадцатеричном виде.
|
\item \code{\%x (\%X)} - беззнаковое целое в шестнадцатеричном виде.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
Как видно, существуют заполнители для всех основных типов данных и для экранирования самого символа начала заполнителя. Заполнители можно и нужно комбинировать в строках как между собой, так и с экранированными последовательностями. Умение работать с заполнителями пригодится не только в консоли, но и при формировании локализованных сообщений в более сложных приложениях, в том числе на других языках.
|
Как видно, существуют заполнители для всех основных типов данных и для экранирования самого символа начала заполнителя. Заполнители можно и нужно комбинировать в строках как между собой, так и с экранированными последовательностями. Умение работать с заполнителями пригодится не только в консоли, но и при формировании локализованных сообщений в более сложных приложениях, в том числе на других языках.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 50;
|
\begin{figure}[h!]
|
||||||
printf("%d\n", a);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("%5d\n", a);
|
int a = 50;
|
||||||
printf("%05d\n", a);
|
printf("%d\n", a);
|
||||||
printf("%.2f\n", 5.12345);
|
printf("%5d\n", a);
|
||||||
printf("Placeholders are \"%5d%%\" of formatting\n", a);
|
printf("%05d\n", a);
|
||||||
\end{lstlisting}
|
printf("%.2f\n", 5.12345);
|
||||||
|
printf("Placeholders are \"%5d%%\" of formatting\n", a);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Так первый оператор выведет просто число. Второй это же число, но оставив для его отображения пять пробельных символов (два окажутся заняты разрядами числа 50, и ещё три останутся свободными, слева от числа). Третий оператор форматированного вывода впишет число в пять отображаемых символов, но заполнит пустоту нулями (запись с лидирующими нулями, leading zeroes). Четвёртый осуществит вывод числа с плавающей точкой, ограничив дробную часть двумя отображаемыми символами, при этом важно, что не произойдёт математического округления, символы просто не отобразятся, такое отображение часто используется для демонстрации денежных значений в долларах и центах, рублях и копейках, и пр. Последний же оператор выведет на экран надпись, информирующую о том, что заполнители - это 50\% форматирования:
|
Так первый оператор выведет просто число. Второй это же число, но оставив для его отображения пять пробельных символов (два окажутся заняты разрядами числа 50, и ещё три останутся свободными, слева от числа). Третий оператор форматированного вывода впишет число в пять отображаемых символов, но заполнит пустоту нулями (запись с лидирующими нулями, leading zeroes). Четвёртый осуществит вывод числа с плавающей точкой, ограничив дробную часть двумя отображаемыми символами, при этом важно, что не произойдёт математического округления, символы просто не отобразятся, такое отображение часто используется для демонстрации денежных значений в долларах и центах, рублях и копейках, и пр. Последний же оператор выведет на экран надпись, информирующую о том, что заполнители - это 50\% форматирования:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
50
|
50
|
||||||
|
@ -66,22 +77,34 @@ $ ./program
|
||||||
Placeholders are " 50%" of formatting
|
Placeholders are " 50%" of formatting
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
Для заполнителей \code{\%d}, \code{\%i}, \code{\%f} часто используются дополнительные параметры, такие как количество знаков после запятой, например, \code{\%.2f} или минимальное количество знаков для отображения целого числа \code{\%5d}.
|
\end{figure}
|
||||||
|
Для заполнителей \code{\%d}, \code{\%i}, \code{\%f} часто используются дополнительные параметры, такие как количество знаков после запятой, например, \code{\%.2f} или минимальное количество знаков для отображения целого числа \code{\%5d}. Также в пользу оператора форматированного вывода говорит тот факт, что, например, в С++ стандартный вывод в консоль осуществляется с помощью команды \code{std::cout}, которая не поддерживала форматирование строк вплоть до принятия стандарта С++20.
|
||||||
\subsection{Форматированный ввод}
|
\subsection{Форматированный ввод}
|
||||||
Поговорив о выводе в консоль нельзя не сказать о пользовательском вводе данных. Один из способов пользовательского ввода данных в программу - это использование функции \code{scanf();}. Предложим пользователю ввести некоторое число:
|
Поговорив о выводе в консоль нельзя не сказать о пользовательском вводе данных. Один из способов пользовательского ввода данных в программу - это использование функции \code{scanf();}. Предложим пользователю ввести некоторое число:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int input;
|
\begin{figure}[h!]
|
||||||
printf("Please, enter a number: ");
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
scanf("%d", &input);
|
int input;
|
||||||
\end{lstlisting}
|
printf("Please, enter a number: ");
|
||||||
|
scanf("%d", &input);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
Функция \code{scanf();} – это функция форматированного ввода. Принцип её работы очень похож на принцип работы функции \code{printf();} В двойных кавычках мы указываем в виде заполнителя тип переменной, которую ожидаем от пользователя, а в качестве дополнительного аргумента указываем адрес той переменной, в которую хотим записать введённые пользователем данные. Получается процесс прямо противоположный выводу. В этой функции можно использовать все те же заполнители, что и при выводе, поэтому пользователь может ввести как целые числа, так и символы, строки и числа с плавающей точкой.
|
Функция \code{scanf();} – это функция форматированного ввода. Принцип её работы очень похож на принцип работы функции \code{printf();} В двойных кавычках мы указываем в виде заполнителя тип переменной, которую ожидаем от пользователя, а в качестве дополнительного аргумента указываем адрес той переменной, в которую хотим записать введённые пользователем данные. Получается процесс прямо противоположный выводу. В этой функции можно использовать все те же заполнители, что и при выводе, поэтому пользователь может ввести как целые числа, так и символы, строки и числа с плавающей точкой.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
printf("You entered %d, let's double it: %d\n", input, input * 2);
|
\begin{figure}[h!]
|
||||||
\end{lstlisting}
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
printf("You entered %d, let's double it: %d\n", input, input * 2);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Выведем в консоль изменённое число, введённое пользователем, чтобы удостовериться, что всё работает. В результате запуска программы, консоль застынет в ожидании пользовательского ввода. Пользователь сообщает консоли (терминалу операционной системы) об окончании ввода нажатием клавиши \code{Enter}:
|
Выведем в консоль изменённое число, введённое пользователем, чтобы удостовериться, что всё работает. В результате запуска программы, консоль застынет в ожидании пользовательского ввода. Пользователь сообщает консоли (терминалу операционной системы) об окончании ввода нажатием клавиши \code{Enter}:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Please, enter a number: 50
|
Please, enter a number: 50
|
||||||
You entered 50, let's double it: 100
|
You entered 50, let's double it: 100
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
|
Функция форматированного ввода позволяет не только приводить пользовательский ввод к нужному типу данных, но и считывать разные сложные пользовательские вводы из множества переменных, разделённых символами.
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
\subsection{Переменные в программе на языке С} Это некие \textit{именованные контейнеры}, тип которых строго описан при их создании, каждый из которых может содержать одно и только одно значение в единицу времени. Названия или имена переменных не могут начинаться с цифр и спецсимволов, а также не должны повторяться (\hyperref[text:naming]{\ref{text:naming}}).
|
\subsection{Переменные в программе на языке С} Это некие \textit{именованные контейнеры}, тип которых строго описан при их создании, каждый из которых может содержать одно и только одно значение в единицу времени. Названия или имена переменных не могут начинаться с цифр и спецсимволов, а также не должны повторяться (\hyperref[text:naming]{\ref{text:naming}}).
|
||||||
\frm{\textbf{Идентификатор} переменной - это её имя, которое для программы не существует без привязки к типизации, то есть для объявления переменной мы пишем её тип и название, что вместе составляет идентификатор. По идентификатору переменной мы можем записать в неё значение, прочитать текущее значение, узнать адрес хранения этого значения, и т.д. \textbf{Литерал} - это число или строка, которые мы пишем в тексте явно. Литералу нельзя присвоить значение, литерал это и есть значение. Литерал (ни строковый, ни числовой) нельзя изменить. Если изменить какой-то литерал, то это будет уже другой литерал, явно изменённое в коде значение. Также есть термины \code{lvalue} и \code{rvalue}. Если очень сильно упрощать, то их можно отождествить с идентификатором и литералом: \code{lvalue} - это то, \textit{куда} присваивается, \code{rvalue} - это то, \textit{что} присваивается}
|
\frm{\textbf{Идентификатор} переменной - это её имя, которое для программы не существует без привязки к типизации, то есть для объявления переменной мы пишем её тип и название, что вместе составляет идентификатор. По идентификатору переменной мы можем записать в неё значение, прочитать текущее значение, узнать адрес хранения этого значения, и т.д. \textbf{Литерал} - это число или строка, которые мы пишем в тексте явно. Литералу нельзя присвоить значение, литерал это и есть значение. Литерал (ни строковый, ни числовой) нельзя изменить. Если изменить какой-то литерал, то это будет уже другой литерал, явно изменённое в коде значение. Также есть термины \code{lvalue} и \code{rvalue}. Если очень сильно упрощать, то их можно отождествить с идентификатором и литералом: \code{lvalue} - это то, \textit{куда} присваивается, \code{rvalue} - это то, \textit{что} присваивается}
|
||||||
Переменные делятся на целочисленные, символьные, указатели и числа с плавающей точкой (англ. floating point, дробное число). Все, кроме указателей и символьных переменных бывают как знаковыми так и беззнаковыми. То есть в знаковых самый старший бит в двоичной записи этих переменных отводится под определение, является ли число отрицательным, или положительным, в беззнаковых все биты используются для записи числа, что увеличивает его диапазон возможных значений, но позволяет записать только положительные числа. В классическом С нет булевого типа, вместо него используется целое число и значения нуля для \textbf{лжи} и \textit{любое} другое число для \textbf{истины}, обычно это единица. Об указателях и булевой алгебре мы будем подробно говорить в одном из последующих разделов.
|
Переменные делятся на целочисленные, символьные, указатели и числа с плавающей точкой (англ. floating point, дробное число). Все, кроме указателей и символьных переменных бывают как знаковыми так и беззнаковыми. То есть в знаковых самый старший бит в двоичной записи этих переменных отводится под определение, является ли число отрицательным, или положительным, в беззнаковых все биты используются для записи числа, что увеличивает его диапазон возможных значений, но позволяет записать только положительные числа. В классическом С нет булевого типа, вместо него используется целое число и значения нуля для \textbf{лжи} и \textit{любое} другое число для \textbf{истины}, обычно это единица. Об указателях и булевой алгебре мы будем подробно говорить в одном из последующих разделов.
|
||||||
\begin{table}[ht]
|
\begin{figure}[h!]
|
||||||
\centering
|
\centering
|
||||||
\begin{tabular}{|p{1.5cm}|p{6.6cm}|p{2.4cm}|}
|
\begin{tabular}{|p{1.5cm}|p{6.6cm}|p{2.4cm}|}
|
||||||
\hline
|
\hline
|
||||||
|
@ -27,60 +27,87 @@
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
\caption{Основные типоы данных в языке С}
|
\caption{Основные типоы данных в языке С}
|
||||||
\label{tab:types}
|
\label{tab:types}
|
||||||
\end{table}
|
\end{figure}
|
||||||
|
\newpage
|
||||||
\paragraph{Символьный тип} не такой простой, как может показаться на первый взгляд. Если вкратце, то в переменной типа \code{char} хранится число, которое можно интерпретировать как символ. По умолчанию тип знаковый, то есть может содержать значения от \textminus128 до +127, но символы в таблице ASCII\footnote{American standard code for interaction interchange}, что совершенно логично, имеют только положительные индексы, поэтому в читаемый текст в стандартном С можно превратить только латинский алфавит и некоторый набор знаков и символов, находящиеся на первых 128-ми местах в этой таблице. Также можно явно указать компилятору, что мы хотим использовать эту переменную как беззнаковую, для этого используется ключевое слово \code{unsigned}, что позволит нам хранить только положительные числа гораздо больших значений. Например для переменной типа \code{unsigned char} это будут значения от 0 до 255, а для переменной типа \code{unsigned int} можно можно хранить значения от 0 до +4.294 миллиардов с какими-то копейками. В более поздних редакциях языка были утверждены типы \code{long long} и другие, для хранения 64-х разрядных целых чисел.
|
\paragraph{Символьный тип} не такой простой, как может показаться на первый взгляд. Если вкратце, то в переменной типа \code{char} хранится число, которое можно интерпретировать как символ. По умолчанию тип знаковый, то есть может содержать значения от \textminus128 до +127, но символы в таблице ASCII\footnote{American standard code for interaction interchange}, что совершенно логично, имеют только положительные индексы, поэтому в читаемый текст в стандартном С можно превратить только латинский алфавит и некоторый набор знаков и символов, находящиеся на первых 128-ми местах в этой таблице. Также можно явно указать компилятору, что мы хотим использовать эту переменную как беззнаковую, для этого используется ключевое слово \code{unsigned}, что позволит нам хранить только положительные числа гораздо больших значений. Например для переменной типа \code{unsigned char} это будут значения от 0 до 255, а для переменной типа \code{unsigned int} можно можно хранить значения от 0 до +4.294 миллиардов с какими-то копейками. В более поздних редакциях языка были утверждены типы \code{long long} и другие, для хранения 64-х разрядных целых чисел.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
unsigned char symbol = 75;
|
\begin{figure}[h!]
|
||||||
printf("75 stands for: %c\n", symbol);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
\end{lstlisting}
|
unsigned char symbol = 75;
|
||||||
Соответственно, программа выше выведет в терминал такую информацию:
|
printf("75 stands for: %c\n", symbol);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
Соответственно, программа выше выведет в терминал информацию о том, какой именно символ в таблице ASCII соответствует позиции 75, по большому счёту, можно было обойтись и без ключевого слова \code{unsigned}, но когда мы говорим именно о символах, а не просто о минимальных по занимаемому размеру целых числах, принято выделять их:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
75 stands for: K
|
75 stands for: K
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
\paragraph{Числа с плавающей точкой (дробные)} представлены двумя типами: четырёхбайтный \code{float} и восьмибайтный \code{double} (также называемый long float). Хранятся в памяти в неявном виде, а разделённые на мантиссу экспоненту и знак, что делает их одними из самых сложных в работе\footnote{Здесь имеется ввиду внутренняя работа самого компьютера, а не работа с такими типами с точки зрения программиста. Именно из-за сложности, многие старые процессорные архитектуры не имели возможности работать с переменными такого типа}. При работе с числами с плавающей точкой нужно обращать особенное внимание на тип переменной, поскольку сравнение внешне одинаковых чисел разных типов с вероятностью 99\% даст ложный результат.
|
\end{figure}
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
\paragraph{Числа с плавающей точкой (дробные)} представлены двумя типами: четырёхбайтный \code{float} и восьмибайтный \code{double} (также называемый long float). Хранятся в памяти в неявном виде, а разделённые на мантиссу экспоненту и знак, что делает их одними из самых сложных в работе\footnote{Здесь имеется ввиду внутренняя работа самого компьютера, а не работа с такими типами с точки зрения программиста. Именно из-за сложности, многие старые процессорные архитектуры не имели возможности работать с переменными такого типа}.
|
||||||
float real = 5,345f; // 4 bytes
|
\label{text:floats}
|
||||||
double realdouble = 5,345; // 8 bytes
|
\frm{Важно, что компилятор, при работе с текстом программы считает все литералы в коде числами типа \code{double}, а значит код вида \code{float var = 0.123;} будет отмечен компилятором как неверный, поскольку компилятор не в состоянии положить восемь байт информации в четырёхбайтную переменную. То есть, если мы хотим инициализировать переменную типа \code{float}, то нам нужно специальным образом пометить литерал: \code{float var = 0.123f;}. Символ \code{f} в конце явно указывает на то, что литерал имеет тип \code{float}.}
|
||||||
printf("float and double: %d\n", real == realdouble);
|
При работе с числами с плавающей точкой нужно обращать особенное внимание на тип переменной, поскольку сравнение внешне одинаковых чисел разных типов с почти стопроцентной вероятностью даст ложный результат, в отличие от сравнения простых целых чисел. Об операциях и операторах сравнения мы поговорим позже, сейчас просто приведём наглядный пример того, как это работает.
|
||||||
|
|
||||||
int a = 10;
|
\begin{figure}[h!]
|
||||||
int b = 10;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("integers: %d\n", a == b);
|
float real = 5,345f; // 4 bytes
|
||||||
\end{lstlisting}
|
double realdouble = 5,345; // 8 bytes
|
||||||
Запустим код и убедимся в этом:
|
printf("float and double: %d\n", real == realdouble);
|
||||||
|
|
||||||
|
int a = 10;
|
||||||
|
int b = 10;
|
||||||
|
printf("integers: %d\n", a == b);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
Запустим код, выводящий результаты сравнений и убедимся в том, что прямое сравнение дробных чисел допускать нежелательно. Обычно, если есть необходимость в сравнении дробных чисел применяют сравнение с некоторой допустимой точностью, например, до третьего или пятого знака после запятой. О таких сравнениях мы поговорим позднее.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
float and double: 0
|
float and double: 0
|
||||||
integers: 1
|
integers: 1
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Тип данных - указатель.} Как было сказано - переменная это именованный контейнер. У каждого такого контейнера есть свой собственный адрес в оперативной памяти. Язык С позволяет узнать этот адрес и работать с ним. Оператор взятия адреса это знак амперсанд (\&), написанный перед именем переменной. То есть у любой переменной всегда есть значение и адрес где это значение хранится. Для вывода в консоль адреса используется специальный заполнитель - \code{\%p}.
|
\paragraph{Тип данных - указатель.} Как было сказано - переменная это именованный контейнер. У каждого такого контейнера есть свой собственный адрес в оперативной памяти. Язык С позволяет узнать этот адрес и работать с ним. Оператор взятия адреса это знак амперсанд (\&), написанный перед именем переменной. То есть у любой переменной всегда есть значение и адрес где это значение хранится. Для вывода в консоль адреса используется специальный заполнитель - \code{\%p}.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
printf("Variable a has value: %d \n", a);
|
\begin{figure}[h!]
|
||||||
printf("Variable a stored at: %p \n", &a);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
\end{lstlisting}
|
printf("Variable a has value: %d \n", a);
|
||||||
|
printf("Variable a stored at: %p \n", &a);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
При неоднократном запуске кода можно обратить внимание, что первые цифры в адресе всегда остаются неизменными, а последние меняются редко, это связано с тем, что в современных операционных системах пользователю для его работы чаще всего выделяется некоторое адресное пространство, которое потом просто переиспользуется и перезаписывается. При запуске точно такого же кода на Вашем компьютере, Вы увидите, что адрес хранения переменной наверняка будет отличаться, это нормально.
|
При неоднократном запуске кода можно обратить внимание, что первые цифры в адресе всегда остаются неизменными, а последние меняются редко, это связано с тем, что в современных операционных системах пользователю для его работы чаще всего выделяется некоторое адресное пространство, которое потом просто переиспользуется и перезаписывается. При запуске точно такого же кода на Вашем компьютере, Вы увидите, что адрес хранения переменной наверняка будет отличаться, это нормально.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Variable a has value: 10
|
Variable a has value: 10
|
||||||
Variable a stored at: 0x7ffe6aa136cf
|
Variable a stored at: 0x7ffe6aa136cf
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\subsection{Базовая арифметика}
|
\subsection{Базовая арифметика}
|
||||||
Раз уж в предыдущем разделе мы коснулись арифметических выражений, поговорим немного об арифметике.
|
Раз уж в предыдущем разделе мы коснулись арифметических выражений, поговорим немного об арифметике.
|
||||||
\paragraph{Простые арифметические операции.} В языке С поддерживаются все базовые арифметические операции, такие как сложение, вычитание, умножение, деление. Операции бинарной арифметики (булевой алгебры), такие как \code{И}, \code{ИЛИ}, \code{НЕ}, \code{ИСКЛЮЧАЮЩЕЕ ИЛИ}, \code{СДВИГИ}. А также все вышеперечисленные операции с последующим присваиванием в первую переменную. Для начала, инициализируем переменную типа \code{int} значением, например, \code{70}, и выведем ее в консоль. Мы можем производить с этой переменной все привычные базовые арифметические манипуляции, ровно также, как мы можем производить эти манипуляции с литералом, который ей присваивается:
|
\paragraph{Простые арифметические операции.} В языке С поддерживаются все базовые арифметические операции, такие как сложение, вычитание, умножение, деление. Операции бинарной арифметики (булевой алгебры), такие как \code{И}, \code{ИЛИ}, \code{НЕ}, \code{ИСКЛЮЧАЮЩЕЕ ИЛИ}, \code{СДВИГИ}. А также все вышеперечисленные операции с последующим присваиванием в первую переменную. Для начала, инициализируем переменную типа \code{int} значением, например, \code{70}, и выведем ее в консоль. Мы можем производить с этой переменной все привычные базовые арифметические манипуляции, ровно также, как мы можем производить эти манипуляции с литералом, который ей присваивается:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int variable = 70;
|
\begin{figure}[h!]
|
||||||
printf("The variable = %d\n", variable);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("The variable = %d\n", variable + 10);
|
int variable = 70;
|
||||||
variable = variable + 50;
|
printf("The variable = %d\n", variable);
|
||||||
printf("The variable = %d\n", variable);
|
printf("The variable = %d\n", variable + 10);
|
||||||
variable = 123 + 50 * 12;
|
variable = variable + 50;
|
||||||
printf("The variable = %d\n", variable);
|
printf("The variable = %d\n", variable);
|
||||||
\end{lstlisting}
|
variable = 123 + 50 * 12;
|
||||||
|
printf("The variable = %d\n", variable);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
То есть, нам доступны операции сложения, умножения, вычитания и деления. Как видно в результате работы программы, есть прямая зависимость между действительным значением переменной и порядком присваивания в неё значения, так в третьем выводе значение равно 120, то есть $70 + 50$, а значит во втором выводе присваивания нового значения $70 + 10$ в переменную \code{variable} не произошло.
|
То есть, нам доступны операции сложения, умножения, вычитания и деления. Как видно в результате работы программы, есть прямая зависимость между действительным значением переменной и порядком присваивания в неё значения, так в третьем выводе значение равно 120, то есть $70 + 50$, а значит во втором выводе присваивания нового значения $70 + 10$ в переменную \code{variable} не произошло.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
The variable = 70
|
The variable = 70
|
||||||
|
@ -89,37 +116,48 @@ The variable = 120
|
||||||
The variable = 723
|
The variable = 723
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Оператор деления} заслуживает особенного внимания, поскольку у него есть два режима работы, отличающие этот оператор от привычной нам арифметики: если мы производим операции с целыми числами такими как \code{int}, \code{short} или \code{char} оператор деления всегда будет возвращать только целые числа, \textbf{отбросив дробную часть}. Это происходит из-за оптимизаций компилятора, то есть если операнды - это целые числа, то и результатом по мнению компьютера может быть только целое число, а из-за строгости типизации мы не можем положить в целочисленную переменную значение с плавающей точкой.
|
\paragraph{Оператор деления} заслуживает особенного внимания, поскольку у него есть два режима работы, отличающие этот оператор от привычной нам арифметики: если мы производим операции с целыми числами такими как \code{int}, \code{short} или \code{char} оператор деления всегда будет возвращать только целые числа, \textbf{отбросив дробную часть}. Это происходит из-за оптимизаций компилятора, то есть если операнды - это целые числа, то и результатом по мнению компьютера может быть только целое число, а из-за строгости типизации мы не можем положить в целочисленную переменную значение с плавающей точкой.
|
||||||
|
|
||||||
Таким образом, отслеживание точности вычислений полностью возлагается на плечи программиста. Важно, что компилятор нам в этом помогает, хоть и не делает всю работу за нас. Компилятор, при преобразовании операций автоматически приводит типы операндов к наиболее подходящему и широкому. То есть, если мы, например, складываем два числа \code{int} и \code{char}, то \code{char} будет автоматически расширен до \code{int}, потому что максимальное значение \code{char} точно поместится в переменную типа \code{int}, а максимальное значение \code{int} точно никак не сможет поместиться в переменную с типом \code{char}. Точно также если умножать \code{int} и \code{float}, то \code{int} будет преобразован во \code{float} по той же причине - \code{int} совсем никак не умеет работать с плавающей точкой, а \code{float} вполне может содержать число без дробной части. Из-за этого языки С/С++ считаются слабо типизированными.
|
Таким образом, отслеживание точности вычислений полностью возлагается на плечи программиста. Важно, что компилятор нам в этом помогает, хоть и не делает всю работу за нас. Компилятор, при преобразовании операций автоматически приводит типы операндов к наиболее подходящему и широкому. То есть, если мы, например, складываем два числа \code{int} и \code{char}, то \code{char} будет автоматически расширен до \code{int}, потому что максимальное значение \code{char} точно поместится в переменную типа \code{int}, а максимальное значение \code{int} точно никак не сможет поместиться в переменную с типом \code{char}. Точно также если умножать \code{int} и \code{float}, то \code{int} будет преобразован во \code{float} по той же причине - \code{int} совсем никак не умеет работать с плавающей точкой, а \code{float} вполне может содержать число без дробной части. Из-за этого языки С/С++ считаются слабо типизированными.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
variable = 10;
|
|
||||||
variable = variable / 3;
|
|
||||||
printf("The variable = %d\n", variable);
|
|
||||||
|
|
||||||
float var = 10;
|
\begin{figure}[h!]
|
||||||
var = var / 3;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("The var = %f\n", var);
|
variable = 10;
|
||||||
\end{lstlisting}
|
variable = variable / 3;
|
||||||
|
printf("The variable = %d\n", variable);
|
||||||
|
|
||||||
|
float var = 10;
|
||||||
|
var = var / 3;
|
||||||
|
printf("The var = %f\n", var);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
В примере выше, обратите внимание, что переменная \code{variable} не была инициализирована, а ей просто было присвоено значение. Это сделано потому, что данный участок кода является продолжением кода примеров простой арифметики из предыдущего параграфа. При использовании оператора деления с целочисленными операндами теряется точность вычислений, что недопустимо.
|
В примере выше, обратите внимание, что переменная \code{variable} не была инициализирована, а ей просто было присвоено значение. Это сделано потому, что данный участок кода является продолжением кода примеров простой арифметики из предыдущего параграфа. При использовании оператора деления с целочисленными операндами теряется точность вычислений, что недопустимо.
|
||||||
\frm{Чтобы оператор деления отработал в не целочисленном режиме, нужно, чтобы хотя бы один операнд был не целочисленным.}
|
\frm{Чтобы оператор деления отработал в не целочисленном режиме, нужно, чтобы хотя бы один операнд был не целочисленным.}
|
||||||
Чаще всего целочисленные переменные используют в качестве счётчиков, индексов и других вспомогательных переменных, поэтому математические операции с ними весьма распространены.
|
Чаще всего целочисленные переменные используют в качестве счётчиков, индексов и других вспомогательных переменных, поэтому математические операции с ними весьма распространены.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
The variable = 3
|
The variable = 3
|
||||||
The var = 3.333333
|
The var = 3.333333
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Деление по модулю.} Также особенного внимания заслуживает оператор получения остатка от деления, иногда называемый оператором взятия по модулю. Записывается как символ \code{\%} и возвращает остаток от целочисленного деления первого числа на второе:
|
\paragraph{Деление по модулю.} Также особенного внимания заслуживает оператор получения остатка от деления, иногда называемый оператором взятия по модулю. Записывается как символ \code{\%} и возвращает остаток от целочисленного деления первого числа на второе:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int remain = variable % 5;
|
\begin{figure}[h!]
|
||||||
printf("Division remainder of %d by %d: %d\n", variable, 5, remain);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
variable = variable + 50;
|
int remain = variable % 5;
|
||||||
printf("The variable = %d\n", variable);
|
printf("Division remainder of %d by %d: %d\n", variable, 5, remain);
|
||||||
variable += 50;
|
variable = variable + 50;
|
||||||
printf("The variable = %d\n", variable);
|
printf("The variable = %d\n", variable);
|
||||||
\end{lstlisting}
|
variable += 50;
|
||||||
|
printf("The variable = %d\n", variable);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Любые арифметические операции можно выполнить с последующим присваиванием в первую переменную. То есть это означает, что запись вида \code{variable = variable + 50;} можно сократить до \code{variable += 50;} и запустив следующий код мы можем убедиться, что начальное значение переменной увеличилось сначала на 50, а затем ещё на 50.
|
Любые арифметические операции можно выполнить с последующим присваиванием в первую переменную. То есть это означает, что запись вида \code{variable = variable + 50;} можно сократить до \code{variable += 50;} и запустив следующий код мы можем убедиться, что начальное значение переменной увеличилось сначала на 50, а затем ещё на 50.
|
||||||
|
\begin{figure}[h]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Division remainder of 3 by 5: 3
|
Division remainder of 3 by 5: 3
|
||||||
|
@ -127,16 +165,22 @@ The variable = 53
|
||||||
The variable = 103
|
The variable = 103
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Инкремент и декремент.} Так, бегло рассмотрев арифметику в языке С нельзя не упомянуть об операторах увеличения и уменьшения значения переменной на единицу с последующим присваиванием. Они называются операторами инкремента (\code{++}) и декремента (\code{\textminus\textminus}). Это унарный оператор, поэтому записывается со своим операндом строго без пробелов: \code{variable++;} и редко используется как самостоятельный оператор на отдельной строке. У операторов инкремента и декремента есть два вида записи: префиксный и постфиксный. Их отличает время применения текущего значения и его изменения. При постфиксной записи, сначала происходит применение текущего результата, а затем его изменение, а при префиксной записи - сначала изменение, а затем применение. Например:
|
\paragraph{Инкремент и декремент.} Так, бегло рассмотрев арифметику в языке С нельзя не упомянуть об операторах увеличения и уменьшения значения переменной на единицу с последующим присваиванием. Они называются операторами инкремента (\code{++}) и декремента (\code{\textminus\textminus}). Это унарный оператор, поэтому записывается со своим операндом строго без пробелов: \code{variable++;} и редко используется как самостоятельный оператор на отдельной строке. У операторов инкремента и декремента есть два вида записи: префиксный и постфиксный. Их отличает время применения текущего значения и его изменения. При постфиксной записи, сначала происходит применение текущего результата, а затем его изменение, а при префиксной записи - сначала изменение, а затем применение. Например:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
variable = 50;
|
\begin{figure}[h!]
|
||||||
printf("1. Postfix increment: %d\n", variable++);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("1. Next line of code: %d\n", variable);
|
variable = 50;
|
||||||
variable = 50;
|
printf("1. Postfix increment: %d\n", variable++);
|
||||||
printf("2. Prefix increment: %d\n", ++variable);
|
printf("1. Next line of code: %d\n", variable);
|
||||||
printf("2. Next line of code: %d\n", variable);
|
variable = 50;
|
||||||
\end{lstlisting}
|
printf("2. Prefix increment: %d\n", ++variable);
|
||||||
|
printf("2. Next line of code: %d\n", variable);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
В результате выполнения этого кода мы можем видеть на экране следующий результат:
|
В результате выполнения этого кода мы можем видеть на экране следующий результат:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
1. Postfix increment: 50
|
1. Postfix increment: 50
|
||||||
|
@ -145,6 +189,7 @@ $ ./program
|
||||||
2. Next line of code: 51
|
2. Next line of code: 51
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\subsection{Булева алгебра и двоичные вычисления}
|
\subsection{Булева алгебра и двоичные вычисления}
|
||||||
\paragraph{Двоичная система счисления} представляет особенный интерес для области информационных технологий, поскольку вся электроника работает по принципу <<есть напряжение или нет напряжения>>, единица или ноль. Все числа из любых систем счисления в результате преобразуются в двоичную и представляются в виде набора единиц и нулей. Так для записи десятичного числа \code{116} используется двоичная запись \code{1110100}. Преобразование из системы счисления с бОльшим основанием в систему счисления с меньшим основанием производится последовательным делением исходного числа на основание системы счисления и записи остатков такого деления в младшие разряды. Например:
|
\paragraph{Двоичная система счисления} представляет особенный интерес для области информационных технологий, поскольку вся электроника работает по принципу <<есть напряжение или нет напряжения>>, единица или ноль. Все числа из любых систем счисления в результате преобразуются в двоичную и представляются в виде набора единиц и нулей. Так для записи десятичного числа \code{116} используется двоичная запись \code{1110100}. Преобразование из системы счисления с бОльшим основанием в систему счисления с меньшим основанием производится последовательным делением исходного числа на основание системы счисления и записи остатков такого деления в младшие разряды. Например:
|
||||||
\[
|
\[
|
||||||
|
@ -221,27 +266,38 @@ $
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
На основе этих знаний мы можем для примера написать программу, меняющую местами значения переменных без использования третьей, вспомогательной и быть уверенными, что переполнения переменных не произойдёт, как это могло бы произойти, например, при использовании сложения и обратного вычитания. Объявим две переменных \code{a} и \code{b}, присвоим им значения и выведем их в консоль. Также подготовим вывод измененных значений \code{a} и \code{b} в консоль:
|
На основе этих знаний мы можем для примера написать программу, меняющую местами значения переменных без использования третьей, вспомогательной и быть уверенными, что переполнения переменных не произойдёт, как это могло бы произойти, например, при использовании сложения и обратного вычитания. Объявим две переменных \code{a} и \code{b}, присвоим им значения и выведем их в консоль. Также подготовим вывод измененных значений \code{a} и \code{b} в консоль:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
char a = 11;
|
\begin{figure}[h!]
|
||||||
char b = 15;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("a = %d, b = %d\n", a, b);
|
char a = 11;
|
||||||
// here will be the swapping algorithm
|
char b = 15;
|
||||||
printf("a = %d, b = %d\n", a, b);
|
printf("a = %d, b = %d\n", a, b);
|
||||||
\end{lstlisting}
|
// here will be the swapping algorithm
|
||||||
|
printf("a = %d, b = %d\n", a, b);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Далее, напишем некую конструкцию, которая при детальном изучении не представляет из себя никакой магии. В переменную \code{а} нужно будет записать результат вычисления \code{a \^{} b}, в переменную \code{b} нужно будет записать результат вычисления \code{b \^{} a} и наконец в переменную \code{а} нужно будет записать результат вычисления \code{a \^{} b}, в коде ниже будет приведена сразу сокращённая запись:
|
Далее, напишем некую конструкцию, которая при детальном изучении не представляет из себя никакой магии. В переменную \code{а} нужно будет записать результат вычисления \code{a \^{} b}, в переменную \code{b} нужно будет записать результат вычисления \code{b \^{} a} и наконец в переменную \code{а} нужно будет записать результат вычисления \code{a \^{} b}, в коде ниже будет приведена сразу сокращённая запись:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
a ^= b;
|
\begin{figure}[h!]
|
||||||
b ^= a;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
a ^= b;
|
a ^= b;
|
||||||
\end{lstlisting}
|
b ^= a;
|
||||||
|
a ^= b;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Нужно сразу оговориться, что этот алгоритм может некорректно работать с одинаковыми и отрицательными числами, это будет зависеть от компилятора, поэтому, если включать этот алгоритм в состав более сложных, лучше осуществлять дополнительные проверки. Вывод этой конкретной программы будет следующим:
|
Нужно сразу оговориться, что этот алгоритм может некорректно работать с одинаковыми и отрицательными числами, это будет зависеть от компилятора, поэтому, если включать этот алгоритм в состав более сложных, лучше осуществлять дополнительные проверки. Вывод этой конкретной программы будет следующим:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
a = 11, b = 15
|
a = 11, b = 15
|
||||||
a = 15, b = 11
|
a = 15, b = 11
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Дополнительно, для написания этого документа был проведён ряд тестов:
|
Дополнительно, для написания этого документа был проведён ряд тестов:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
Test project ~/Documents/c-basic/build
|
Test project ~/Documents/c-basic/build
|
||||||
1/7 Test #1: Swap.TwoPosNumbers ..... Passed 0.00 sec
|
1/7 Test #1: Swap.TwoPosNumbers ..... Passed 0.00 sec
|
||||||
|
@ -255,40 +311,55 @@ Test project ~/Documents/c-basic/build
|
||||||
86% tests passed, 1 tests failed out of 7
|
86% tests passed, 1 tests failed out of 7
|
||||||
Total Test time (real) = 0.04 sec
|
Total Test time (real) = 0.04 sec
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Ожидаемо, не прошёл тест, в котором присутствовало переполнение переменной, этот случай также был отмечен предупреждением компилятора о том, что программист пытается присвоить переменной значение, большее, чем переменная способна вместить.
|
Ожидаемо, не прошёл тест, в котором присутствовало переполнение переменной, этот случай также был отмечен предупреждением компилятора о том, что программист пытается присвоить переменной значение, большее, чем переменная способна вместить.
|
||||||
\frm{Здесь был преднамеренно использован тест с провальным результатом, для более явной демонстрации происходящего внутри алгоритма, и потому что нам не нужна дальнейшая компиляция продакшн кода. Обычно, тест-кейсы с ожидаемым провалом пишутся с инверсией проверки, то есть, если мы ожидаем, что некоторые значения не будут равны эталонным, необходимо проверять значения на неравенство, таким образом все тест-кейсы пройдут успешно.}
|
\frm{Здесь был преднамеренно использован тест с провальным результатом, для более явной демонстрации происходящего внутри алгоритма, и потому что нам не нужна дальнейшая компиляция продакшн кода. Обычно, тест-кейсы с ожидаемым провалом пишутся с инверсией проверки, то есть, если мы ожидаем, что некоторые значения не будут равны эталонным, необходимо проверять значения на неравенство, таким образом все тест-кейсы пройдут успешно.}
|
||||||
Рассмотрим происходящее для приведённого примера кода пошагово: оператор \code{ИСКЛЮЧАЮЩЕГО ИЛИ} выполняется следующим образом: результат будет равен \code{1} если операнды (в данном случае, разряды двоичного представления числа) различаются и \code{0} если они совпадают. Изначально имеем две переменных, \code{a} и \code{b} - число \code{11} типа \code{char} (в двоичном представлении это \code{00001011}), и число \code{15} (это \code{00001111}). В коде ниже можно наглядно рассмотреть работу оператора \code{ИСКЛЮЧАЮЩЕГО ИЛИ}:
|
Рассмотрим происходящее для приведённого примера кода пошагово: оператор \code{ИСКЛЮЧАЮЩЕГО ИЛИ} выполняется следующим образом: результат будет равен \code{1} если операнды (в данном случае, разряды двоичного представления числа) различаются и \code{0} если они совпадают. Изначально имеем две переменных, \code{a} и \code{b} - число \code{11} типа \code{char} (в двоичном представлении это \code{00001011}), и число \code{15} (это \code{00001111}). В коде ниже можно наглядно рассмотреть работу оператора \code{ИСКЛЮЧАЮЩЕГО ИЛИ}:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
// a = 11 (00001011)
|
// a = 11 (00001011)
|
||||||
// b = 15 (00001111)
|
// b = 15 (00001111)
|
||||||
a = a ^ b; //00000100
|
a = a ^ b; //00000100
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
После выполнения первого оператора в переменную \code{a} будет положено промежуточное число \code{00000100} – это цифра \code{4}, а в переменной \code{b} останется число \code{15 (00001111)}. Ко второму действию мы приходим с начальными значениями: \code{a = 4 (00000100)}, \code{b = 15 (00001111)}, производим операцию \code{ИСКЛЮЧАЮЩЕГО ИЛИ} с последующим присваиванием в переменную \code{b} и получаем \code{00001011} – т.е. \code{b = 11 (00001011)}.
|
После выполнения первого оператора в переменную \code{a} будет положено промежуточное число \code{00000100} – это цифра \code{4}, а в переменной \code{b} останется число \code{15 (00001111)}. Ко второму действию мы приходим с начальными значениями: \code{a = 4 (00000100)}, \code{b = 15 (00001111)}, производим операцию \code{ИСКЛЮЧАЮЩЕГО ИЛИ} с последующим присваиванием в переменную \code{b} и получаем \code{00001011} – т.е. \code{b = 11 (00001011)}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
// b = 15 (00001111)
|
// b = 15 (00001111)
|
||||||
// a = 4 (00000100)
|
// a = 4 (00000100)
|
||||||
b = b ^ a; //00001011
|
b = b ^ a; //00001011
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
И после выполнения третьего оператора \code{ИСКЛЮЧАЮЩЕГО ИЛИ} в переменную \code{a} будет положено значение \code{00001111} – это цифра \code{15}.
|
И после выполнения третьего оператора \code{ИСКЛЮЧАЮЩЕГО ИЛИ} в переменную \code{a} будет положено значение \code{00001111} – это цифра \code{15}.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
// a = 4 (00000100)
|
// a = 4 (00000100)
|
||||||
// b = 11 (00001011)
|
// b = 11 (00001011)
|
||||||
a = a ^ b; //00001111
|
a = a ^ b; //00001111
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
\paragraph{Операции сдвига} бывают логические, арифметические и циклические. В языке С реализован логический сдвиг, то есть недостающие разряды при сдвиге заполняются нулями. Итак, допустим, что нам нужно переменную \code{a} сдвинуть влево на \code{3} бита, на самом деле это означает что мы переменную \code{a} умножим на $2^3$. А переменную \code{b} мы сдвинем вправо на \code{2} бита, это означает, что мы переменную \code{b} разделим на $2^2$, при этом важно упомянуть, что будет произведено целочисленное деление.
|
\end{figure}
|
||||||
|
\paragraph{Операции сдвига} бывают логические, арифметические и циклические. В языке С реализован логический сдвиг, то есть недостающие разряды при сдвиге заполняются нулями, а выходящие за пределы хранения переменной теряются без возможности восстановления. Итак, допустим, что нам нужно переменную \code{a} сдвинуть влево на \code{3} бита, на самом деле это означает что мы переменную \code{a} умножим на $2^3$. В примере ниже, переменную \code{b} мы сдвинем вправо на \code{2} бита, это означает, что мы переменную \code{b} разделим на $2^2$, при этом важно упомянуть, что будет произведено целочисленное деление.
|
||||||
\frm{\textbf{Сдвиг влево} числа k на n - это \textit{умножение} $k*2^n$. \textbf{Сдвиг вправо} числа k на n - это \textit{целочисленное деление} $\frac{k}{2^n}$.}
|
\frm{\textbf{Сдвиг влево} числа k на n - это \textit{умножение} $k*2^n$. \textbf{Сдвиг вправо} числа k на n - это \textit{целочисленное деление} $\frac{k}{2^n}$.}
|
||||||
Это тоже самое что записать $a * 8$ и $b / 4$. Просто на маломощных компьютерах выполнится это гораздо быстрее. Бинарная алгебра это большая и сложная тема.
|
Это тоже самое что записать $a * 8$ и $b / 4$. Просто на маломощных компьютерах выполнится это гораздо быстрее. Бинарная алгебра это большая и интересная тема, на которую написано немало статей и даже книг, но подробное её изучение выходит далеко за рамки знакомства с синтаксическими основами языка. Также важно помнить, что бинарная алгебра не работает с дробными числами по причине их сложного (\hyperref[text:floats]{\ref{text:floats}}) хранения.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
a = 15; //00001111
|
\begin{figure}[h!]
|
||||||
b = 11; //00001011
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("a = %d", a);
|
a = 15; //00001111
|
||||||
a = a << 3; // 15 * 8
|
b = 11; //00001011
|
||||||
printf("a = %d", a); //a = 120; //01111000
|
printf("a = %d", a);
|
||||||
printf("b = %d", b);
|
a = a << 3; // 15 * 8
|
||||||
b = b >> 2; // 11 / 4
|
printf("a = %d", a); //a = 120; //01111000
|
||||||
printf("b = %d", b); //b = 2; //00000010
|
printf("b = %d", b);
|
||||||
\end{lstlisting}
|
b = b >> 2; // 11 / 4
|
||||||
Применять бинарную алгебру можно и в больших проектах, работающих со сложными высокоуровневыми абстракциями. Поддержка бинарных операций есть в подавляющем числе языков программирования.
|
printf("b = %d", b); //b = 2; //00000010
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
Применять бинарную алгебру можно и в больших проектах, работающих со сложными высокоуровневыми абстракциями. Помимо этого важно помнить, что поддержка бинарных операций есть в подавляющем числе языков программирования. Используя бинарную алгебру можно создавать оптимальные протоколы передачи данных и/или алгоритмы хранения и обработки.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
a = 15
|
a = 15
|
||||||
|
@ -297,3 +368,5 @@ b = 11
|
||||||
b = 2
|
b = 2
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
|
Тема битовых операций постепенно теряет свою актуальность, в связи с развитием технологий, скоростей, объёмов. Скорее всего, битовые операции в ближайшем будущем перейдут в разряд узкоспециальных знаний и будут применяться только при программировании микроконтроллеров, несмотря на то, что работа с двоичным представлением чисел открывает перед программистом широкий простор к оптимизации и ускорению собственного кода.
|
||||||
|
|
|
@ -3,36 +3,47 @@
|
||||||
\paragraph{\code{if()}} пожалуй, самый часто используемый в любом языке программирования, в том числе и в языке С оператор. Оператор \code{if()} позволяет программе принять решение о выполнении или невыполнении того или иного действия в зависимости от текущего состояния. В случае, если условие в круглых скобках выполнится, выполнится и последующий код, который чаще всего пишется в фигурных скобках. Если условие в круглых скобках не выполнится, то все операторы внутри идущих следом фигурных скобок будут проигнорированы.
|
\paragraph{\code{if()}} пожалуй, самый часто используемый в любом языке программирования, в том числе и в языке С оператор. Оператор \code{if()} позволяет программе принять решение о выполнении или невыполнении того или иного действия в зависимости от текущего состояния. В случае, если условие в круглых скобках выполнится, выполнится и последующий код, который чаще всего пишется в фигурных скобках. Если условие в круглых скобках не выполнится, то все операторы внутри идущих следом фигурных скобок будут проигнорированы.
|
||||||
|
|
||||||
Например, зададим пользователю вопрос, хочет ли он, чтобы его поприветствовали, для этого опишем переменную \code{char answer}, которая будет хранить ответ пользователя в виде символа и спросим у пользователя в терминале, хочет ли он, чтобы мы его поприветствовали, выведем на экран строку с приглашением. Далее при помощи уже знакомой нам функции \code{scanf();} считаем ответ пользователя в переменную, и, в зависимости от пользовательского ввода, программа либо поприветствует пользователя, либо нет, это решение будет принято с помощью оператора \code{if()}.
|
Например, зададим пользователю вопрос, хочет ли он, чтобы его поприветствовали, для этого опишем переменную \code{char answer}, которая будет хранить ответ пользователя в виде символа и спросим у пользователя в терминале, хочет ли он, чтобы мы его поприветствовали, выведем на экран строку с приглашением. Далее при помощи уже знакомой нам функции \code{scanf();} считаем ответ пользователя в переменную, и, в зависимости от пользовательского ввода, программа либо поприветствует пользователя, либо нет, это решение будет принято с помощью оператора \code{if()}.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
char answer;
|
|
||||||
printf("do you want me to salute you (y/n)? ");
|
|
||||||
|
|
||||||
scanf ("%s", &answer);
|
\begin{figure}[h!]
|
||||||
if (answer == 'y') {
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("Hello, user");
|
char answer;
|
||||||
}
|
printf("do you want me to salute you (y/n)? ");
|
||||||
\end{lstlisting}
|
|
||||||
|
scanf ("%s", &answer);
|
||||||
|
if (answer == 'y') {
|
||||||
|
printf("Hello, user");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
\paragraph{\code{else}} Зачастую складываются ситуации, когда нужно выполнить разные наборы действий, в зависимости от результата проверки условия. Для таких случаев используется дополнение к оператору \code{if()} - оператор \code{else}, в котором описывается последовательность действий выполняемая в случае если условие в круглых скобках дало ложный результат. Код немного изменится, не будем приводить повторяющиеся части, взаимодействия с пользователем, сконцентрируемся на условном операторе:
|
\paragraph{\code{else}} Зачастую складываются ситуации, когда нужно выполнить разные наборы действий, в зависимости от результата проверки условия. Для таких случаев используется дополнение к оператору \code{if()} - оператор \code{else}, в котором описывается последовательность действий выполняемая в случае если условие в круглых скобках дало ложный результат. Код немного изменится, не будем приводить повторяющиеся части, взаимодействия с пользователем, сконцентрируемся на условном операторе:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
if (answer == 'y') {
|
\begin{figure}[h!]
|
||||||
printf("Hello, user");
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
} else {
|
if (answer == 'y') {
|
||||||
printf("I didn't want to salute you anyway");
|
printf("Hello, user");
|
||||||
}
|
} else {
|
||||||
\end{lstlisting}
|
printf("I didn't want to salute you anyway");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Как вы видите, в зависимости от того что ввел пользователь мы реализуем ту или иную ветку оператора \code{if}-\code{else}. Конструкция \code{if}-\code{else} является единым оператором выбора, то есть выполнив код в фигурных скобках после \code{if} программа не станет выполнять код в \code{else}, и наоборот.
|
Как вы видите, в зависимости от того что ввел пользователь мы реализуем ту или иную ветку оператора \code{if}-\code{else}. Конструкция \code{if}-\code{else} является единым оператором выбора, то есть выполнив код в фигурных скобках после \code{if} программа не станет выполнять код в \code{else}, и наоборот.
|
||||||
|
|
||||||
\paragraph{\code{else if()}} Множественный выбор при помощи оператора \code{if} можно осуществить используя конструкцию \code{if}-\code{else if}-\code{else}. Данное усложнение также будет являться единым оператором выбора. Добавим в нашу конструкцию еще одно условие и опишем поведение для ответа <<да>> и ответа <<нет>>. В этом примере оператором \code{else} будет непонимание программы того что ввел пользователь. Выведем в консоль надпись <<Я не могу понять Ваш ввод>>.
|
\paragraph{\code{else if()}} Множественный выбор при помощи оператора \code{if} можно осуществить используя конструкцию \code{if}-\code{else if}-\code{else}. Данное усложнение также будет являться единым оператором выбора. Добавим в нашу конструкцию еще одно условие и опишем поведение для ответа <<да>> и ответа <<нет>>. В этом примере оператором \code{else} будет непонимание программы того что ввел пользователь. Выведем в консоль надпись <<Я не могу понять Ваш ввод>>.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
if (answer == 'y') {
|
\begin{figure}[h!]
|
||||||
printf("Hello, user");
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
} else if (answer == 'n') {
|
if (answer == 'y') {
|
||||||
printf("I didn't want to salute you anyway");
|
printf("Hello, user");
|
||||||
} else {
|
} else if (answer == 'n') {
|
||||||
printf("I can't understand your input");
|
printf("I didn't want to salute you anyway");
|
||||||
}
|
} else {
|
||||||
\end{lstlisting}
|
printf("I can't understand your input");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Операторов \code{else if} в одном операторе выбора может быть сколько угодно, в отличие от оператора \code{if} и оператора \code{else} которых не может быть больше одного.
|
Операторов \code{else if} в одном операторе выбора может быть сколько угодно, в отличие от оператора \code{if} и оператора \code{else} которых не может быть больше одного.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
do you want me to salute you (y/n)? y
|
do you want me to salute you (y/n)? y
|
||||||
|
@ -45,82 +56,117 @@ do you want me to salute you (y/n)? x
|
||||||
I can't understand your input
|
I can't understand your input
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Тернарный оператор.} Для короткой или внутристрочной записи условного оператора, а также для присваивания переменных по условию можно использовать \textbf{тернарный оператор}, также называемый оператором условного перехода и записываемый с помощью следующего синтаксиса: \code{(условие) ? истина : ложь}. Например, создадим три целочисленные переменные \code{а}, \code{b}, \code{c} и зададим двум из них какие-нибудь начальные значения, допустим \code{а = 10} и \code{b = 15}. Поставим себе задачу: присвоить переменной \code{c} наименьшее из значений \code{а} или \code{b}. Если мы будем использовать только что изученный нами оператор \code{if}-\code{else} у нас должен получиться такой код:
|
\paragraph{Тернарный оператор.} Для короткой или внутристрочной записи условного оператора, а также для присваивания переменных по условию можно использовать \textbf{тернарный оператор}, также называемый оператором условного перехода и записываемый с помощью следующего синтаксиса: \code{(условие) ? истина : ложь}. Например, создадим три целочисленные переменные \code{а}, \code{b}, \code{c} и зададим двум из них какие-нибудь начальные значения, допустим \code{а = 10} и \code{b = 15}. Поставим себе задачу: присвоить переменной \code{c} наименьшее из значений \code{а} или \code{b}. Если мы будем использовать только что изученный нами оператор \code{if}-\code{else} у нас должен получиться такой код:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 10;
|
|
||||||
int b = 15;
|
|
||||||
int c;
|
|
||||||
if (a > b) {
|
|
||||||
c = b;
|
|
||||||
} else {
|
|
||||||
c = a;
|
|
||||||
}
|
|
||||||
\end{lstlisting}
|
|
||||||
Запись условного оператора можно значительно сократить, поскольку в теле оператора происходит выбор: какое значение присвоить в \code{c} по условию в круглых скобках. Условие оставляем, а \code{а} и \code{b} переносим в секции истины и лжи, соответственно. Получим запись вида:
|
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 10;
|
|
||||||
int b = 15;
|
|
||||||
int c = (a > b) ? b : a;
|
|
||||||
\end{lstlisting}
|
|
||||||
которая будет обозначать, что в случае если \code{a > b}, в переменную \code{c} запишется значение {b}, и наоборот если \code{b > a}, то в переменную \code{c} запишется значение \code{а}.
|
|
||||||
|
|
||||||
Также тернарный оператор можно использовать для удобного форматированного вывода, например опишем функцию \code{printf();} которая будет печатать нам строку, и в зависимости от условия, это будет \code{"true"} либо \code{"false"}:
|
\begin{figure}[h!]
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("%s", (1 > 0) ? "true" : "false");
|
int a = 10;
|
||||||
\end{lstlisting}
|
int b = 15;
|
||||||
|
int c;
|
||||||
|
if (a > b) {
|
||||||
|
c = b;
|
||||||
|
} else {
|
||||||
|
c = a;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
Запись условного оператора можно значительно сократить, поскольку в теле оператора происходит выбор: какое значение присвоить в \code{c} по условию в круглых скобках. Условие оставляем, а \code{а} и \code{b} переносим в секции истины и лжи, соответственно. Получим запись вида:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
int a = 10;
|
||||||
|
int b = 15;
|
||||||
|
int c = (a > b) ? b : a;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
которая будет обозначать, что в случае если \code{a > b}, в переменную \code{c} запишется значение {b}, и наоборот если \code{b > a}, то в переменную \code{c} запишется значение \code{а}. Также тернарный оператор можно использовать для удобного форматированного вывода, например опишем функцию \code{printf();} которая будет печатать нам строку, и в зависимости от условия, это будет \code{"true"} либо \code{"false"}:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
printf("%s", (1 > 0) ? "true" : "false");
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Проверим как это работает, и в результате видим true, потому что единица действительно больше нуля.
|
Проверим как это работает, и в результате видим true, потому что единица действительно больше нуля.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
true
|
true
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\paragraph{Вложенные условия и сокращения}
|
\paragraph{Вложенные условия и сокращения}
|
||||||
Внутри фигурных скобок конструкций \code{if()} находится код программы, поэтому там могут находиться и другие условные операторы. Условия, расположенные таким образом называются вложенными. Никаких ограничений на использование вложенных условий в языке С нет. В примере ниже показано, что условия всегда выполняются (единица в круглых скобках будет означать, что условие всегда истинно), а комментариями с многоточием показано, где может располагаться код программы.
|
Внутри фигурных скобок конструкций \code{if()} находится код программы, поэтому там могут находиться и другие условные операторы. Условия, расположенные таким образом называются вложенными. Никаких ограничений на использование вложенных условий в языке С нет. В примере ниже показано, что условия всегда выполняются (единица в круглых скобках будет означать, что условие всегда истинно), а комментариями с многоточием показано, где может располагаться код программы.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
if (1) {
|
\begin{figure}[h!]
|
||||||
// operators
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
if (1) {
|
if (1) {
|
||||||
// operators
|
// operators
|
||||||
|
if (1) {
|
||||||
|
// operators
|
||||||
|
}
|
||||||
|
// operators
|
||||||
}
|
}
|
||||||
// operators
|
\end{lstlisting}
|
||||||
}
|
\end{figure}
|
||||||
\end{lstlisting}
|
|
||||||
Также важно отметить, что если после условных операторов следует только один оператор языка, как в примере ниже, то использование фигурных скобок не обязательно, хотя и считается хорошим тоном писать их всегда:
|
Также важно отметить, что если после условных операторов следует только один оператор языка, как в примере ниже, то использование фигурных скобок не обязательно, хотя и считается хорошим тоном писать их всегда:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
// one operator condirion
|
|
||||||
if (1) {
|
|
||||||
// single operator
|
|
||||||
}
|
|
||||||
|
|
||||||
// also one operator condition
|
\begin{figure}[h!]
|
||||||
if (1)
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
// single operator
|
// one operator condirion
|
||||||
|
if (1) {
|
||||||
|
// single operator
|
||||||
|
}
|
||||||
|
|
||||||
// another one operator condition
|
// also one operator condition
|
||||||
if (1)
|
if (1)
|
||||||
// single operator
|
// single operator
|
||||||
else
|
|
||||||
// single operator
|
// another one operator condition
|
||||||
\end{lstlisting}
|
if (1)
|
||||||
|
// single operator
|
||||||
|
else
|
||||||
|
// single operator
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
При использовании такой записи можно легко допустить ошибку: забыть о необходимости объединения кода фигурными скобками, и предполагать, что несколько операторов могут выполниться по условию без фигурных скобок.
|
При использовании такой записи можно легко допустить ошибку: забыть о необходимости объединения кода фигурными скобками, и предполагать, что несколько операторов могут выполниться по условию без фигурных скобок.
|
||||||
\frm{Часто это заблуждение приводит к трудноуловимым ошибкам в коде, когда программа компилируется, запускается, но работает не так, как следует.}
|
\frm{Часто это заблуждение приводит к трудноуловимым ошибкам в коде, когда программа компилируется, запускается, но работает не так, как следует.}
|
||||||
\subsection{Операции сравнения}
|
\subsection{Операции сравнения}
|
||||||
\paragraph{Арифметическое сравнение} это знакомые и привычные нам со школы операторы <<больше>> (\code{>}), <<меньше>> (\code{<}), <<больше или равно>> (\code{>=}), <<меньше или равно>> (\code{<=}), а также в языке С присутствуют в виде отдельных операторов <<проверка на равенство>>, которая записывается в виде двух знаков равенства (\code{==}), и <<проверка на неравенство>>, которая записывается в виде восклицательного знака и символа равенства (\code{!=}). Возвращают истину, когда выполняются соответствующие названиям условия и ложь, когда условия не выполняются, что очевидно.
|
\paragraph{Арифметическое сравнение} это знакомые и привычные нам со школы операторы <<больше>> (\code{>}), <<меньше>> (\code{<}), <<больше или равно>> (\code{>=}), <<меньше или равно>> (\code{<=}), а также в языке С присутствуют в виде отдельных операторов <<проверка на равенство>>, которая записывается в виде двух знаков равенства (\code{==}), и <<проверка на неравенство>>, которая записывается в виде восклицательного знака и символа равенства (\code{!=}). Возвращают истину, когда выполняются соответствующие названиям условия и ложь, когда условия не выполняются, что очевидно. Единственное исключение, которое было оговорено ранее (\hyperref[text:floats]{\ref{text:floats}}), сравнение разнотипных нецелочисленных значений должно осуществляться через сравнение допустимой дельты этих значений, например:
|
||||||
\paragraph{Логических операторов} три: это \code{И (\&\&)}, \code{ИЛИ (||)}, \code{НЕ (!)}. В отличие от арифметических двоичных операторов - логические возвращают истину или ложь т.е. в случае языка С - \code{1} либо \code{0} и работают с операндами слева и справа целиком, а не поразрядно. В этом легко убедиться, попытавшись вывести на экран результат сравнения с заведомо неверным форматированием и получив ошибку, говорящую о том, что компилятор ожидал от нас число, а мы хотим его отформатировать в строку.
|
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
\begin{figure}[h!]
|
||||||
printf("%s\n", 1 == 1);
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
\end{lstlisting}
|
float f = 0.5f;
|
||||||
|
double d = 0.5;
|
||||||
|
fouble diff = (f > d) ? f - d : d - f;
|
||||||
|
if (diff < 0.00001) {
|
||||||
|
// useful code here
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
\paragraph{Логические операторы}
|
||||||
|
Их три: это \code{И (\&\&)}, \code{ИЛИ (||)}, \code{НЕ (!)}. В отличие от арифметических двоичных операторов - логические возвращают истину или ложь т.е. в случае языка С - \code{1} либо \code{0} и работают с операндами слева и справа целиком, а не поразрядно. В этом легко убедиться, попытавшись вывести на экран результат сравнения с заведомо неверным форматированием и получив ошибку, говорящую о том, что компилятор ожидал от нас число, а мы хотим его отформатировать в строку.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
printf("%s\n", 1 == 1);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Некоторые компиляторы выдают ошибку на этапе компиляции, некоторые компилируют такой код, но программа не сможет выполниться и выдаст ошибку \textbf{Segmentation fault}. Это зависит от большого количества факторов, таких как тип операционной системы, тип и версия компилятора, версия используемого стандарта языка.
|
Некоторые компиляторы выдают ошибку на этапе компиляции, некоторые компилируют такой код, но программа не сможет выполниться и выдаст ошибку \textbf{Segmentation fault}. Это зависит от большого количества факторов, таких как тип операционной системы, тип и версия компилятора, версия используемого стандарта языка.
|
||||||
|
|
||||||
Отдельного внимания заслуживает применение оператора поразрядного (арифметического) \code{ИСКЛЮЧАЮЩЕГО ИЛИ} в качестве логического. В случае такого применения оператор \code{ИСКЛЮЧАЮЩЕГО ИЛИ}, фактически, дублирует сравнение на неравенство, это легко объяснить, проведя анализ происходящего с числами при таком сравнении:
|
Отдельного внимания заслуживает применение оператора поразрядного (арифметического) \code{ИСКЛЮЧАЮЩЕГО ИЛИ} в качестве логического. В случае такого применения оператор \code{ИСКЛЮЧАЮЩЕГО ИЛИ}, фактически, дублирует сравнение на неравенство, это легко объяснить, проведя анализ происходящего с числами при таком сравнении:
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 11; //00001011
|
\begin{figure}[h!]
|
||||||
int b = 11; //00001011
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
// ^ 00000000
|
int a = 11; //00001011
|
||||||
if (a ^ b) {
|
int b = 11; //00001011
|
||||||
printf("numbers are not equal\n");
|
// ^ 00000000
|
||||||
}
|
if (a ^ b) {
|
||||||
\end{lstlisting}
|
printf("numbers are not equal\n");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Данный код внутри фигурных скобок оператора \code{if()} никогда не выполнится, поскольку в С оператор сравнения работает с числами, интерпретируя ноль как ложь, а любое ненулевое значение как истину, мы наблюдаем, что побитовое \code{ИСКЛЮЧАЮЩЕЕ ИЛИ} - это тоже самое, что проверка на неравенство.
|
Данный код внутри фигурных скобок оператора \code{if()} никогда не выполнится, поскольку в С оператор сравнения работает с числами, интерпретируя ноль как ложь, а любое ненулевое значение как истину, мы наблюдаем, что побитовое \code{ИСКЛЮЧАЮЩЕЕ ИЛИ} - это тоже самое, что проверка на неравенство.
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item оператор \code{И (\&\&)} возвращает истину только когда оба операнда истинны;
|
\item оператор \code{И (\&\&)} возвращает истину только когда оба операнда истинны;
|
||||||
|
@ -175,30 +221,41 @@ if (a ^ b) {
|
||||||
\item $||$
|
\item $||$
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
Приведём короткий пример: дана некоторая целочисленная переменная и нужно выяснить, не выходит ли эта переменная за рамки заданных значений, например от нуля до десяти. Условие можно будет скомбинировать так: \code{x >= 0 \&\& x <= 10}. В случае его истинности - выдадим сообщение о том, что \code{х} подходит.
|
Приведём короткий пример: дана некоторая целочисленная переменная и нужно выяснить, не выходит ли эта переменная за рамки заданных значений, например от нуля до десяти. Условие можно будет скомбинировать так: \code{x >= 0 \&\& x <= 10}. В случае его истинности - выдадим сообщение о том, что \code{х} подходит.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int x = 7;
|
\begin{figure}[h!]
|
||||||
if ((x >= 0) && (x <= 10)) {
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("X Fits!\n");
|
int x = 7;
|
||||||
}
|
if ((x >= 0) && (x <= 10)) {
|
||||||
\end{lstlisting}
|
printf("X Fits!\n");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
В данной записи мы видим, что сначала \code{х} сравнивается с нулём, затем с десятью, и в конце результаты будут сравнены между собой.
|
В данной записи мы видим, что сначала \code{х} сравнивается с нулём, затем с десятью, и в конце результаты будут сравнены между собой.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
X Fits!
|
X Fits!
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Самый не приоритетный оператор тернарный, внимательный читатель мог заметить, что он даже не вошёл в список выше, это сделано, поскольку использование тернарного оператора внутри условий нежелательно. Тернарный оператор внутри условий резко снижает читаемость кода и усложняет его интерпретацию. Если Вы сомневаетесь в приоритете сравнений или Вам необходимо описать какое-то очень сложное условие, всегда можно воспользоваться простыми математическими круглыми скобками, задав приоритет операций явно. В таком случае в первую очередь будут выполнены операции в скобках.
|
Самый не приоритетный оператор тернарный, внимательный читатель мог заметить, что он даже не вошёл в список выше, это сделано, поскольку использование тернарного оператора внутри условий нежелательно. Тернарный оператор внутри условий резко снижает читаемость кода и усложняет его интерпретацию. Если Вы сомневаетесь в приоритете сравнений или Вам необходимо описать какое-то очень сложное условие, всегда можно воспользоваться простыми математическими круглыми скобками, задав приоритет операций явно. В таком случае в первую очередь будут выполнены операции в скобках.
|
||||||
\subsection{Блоки кода и область видимости}
|
\subsection{Блоки кода и область видимости}
|
||||||
Говоря об операторах языка С и управляющих конструкциях нельзя не сказать о <<блоках кода>> и <<областях видимости>>. Как видно, условные операторы содержат записи в фигурных скобках. В такие же скобки заключён код функции \code{main}. Эти скобки называются <<операторными>>, а то что в них содержится, называется <<блоком кода>> или <<телом>> оператора или функции. Все переменные, которые инициализируются внутри блока кода, существуют и <<видны>> только внутри кодового блока. Поэтому пространство между операторными скобками также называют <<областью видимости>>.
|
Говоря об операторах языка С и управляющих конструкциях нельзя не сказать о <<блоках кода>> и <<областях видимости>>. Как видно, условные операторы содержат записи в фигурных скобках. В такие же скобки заключён код функции \code{main}. Эти скобки называются <<операторными>>, а то что в них содержится, называется <<блоком кода>> или <<телом>> оператора или функции. Все переменные, которые инициализируются внутри блока кода, существуют и <<видны>> только внутри кодового блока. Поэтому пространство между операторными скобками также называют <<областью видимости>>.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int x = 7;
|
\begin{figure}[h!]
|
||||||
if ((x >= 0) && (x <= 10)) {
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
int var = 0;
|
int x = 7;
|
||||||
printf("X Fits!\n");
|
if ((x >= 0) && (x <= 10)) {
|
||||||
}
|
int var = 0;
|
||||||
printf("%d", var);
|
printf("X Fits!\n");
|
||||||
\end{lstlisting}
|
}
|
||||||
|
printf("%d", var);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
На этом примере можно увидеть, что мы не можем напечатать значение переменной \code{var}, поскольку она была создана внутри блока кода оператора \code{if()} и перестала существовать для нашей программы как только мы вышли за его пределы. Такой код даже не скомпилируется:
|
На этом примере можно увидеть, что мы не можем напечатать значение переменной \code{var}, поскольку она была создана внутри блока кода оператора \code{if()} и перестала существовать для нашей программы как только мы вышли за его пределы. Такой код даже не скомпилируется:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ gcc -o program main.c
|
$ gcc -o program main.c
|
||||||
error: 'var' undeclared (first use in this function);
|
error: 'var' undeclared (first use in this function);
|
||||||
|
@ -208,3 +265,4 @@ printf("%d", var);
|
||||||
char
|
char
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
|
|
|
@ -7,53 +7,70 @@
|
||||||
Базовый цикл в языке С записывается при помощи ключевого слова \code{while()} после которого в круглых скобках пишется условие. При истинности данного условия будет выполняться тело цикла, которое в свою очередь пишется в фигурных скобках. Общий внешний вид очень похож на условный оператор, с той лишь разницей, что по окончании выполнения операторов внутри фигурных скобок мы переходим не на следующую строку, а обратно в проверку условия.
|
Базовый цикл в языке С записывается при помощи ключевого слова \code{while()} после которого в круглых скобках пишется условие. При истинности данного условия будет выполняться тело цикла, которое в свою очередь пишется в фигурных скобках. Общий внешний вид очень похож на условный оператор, с той лишь разницей, что по окончании выполнения операторов внутри фигурных скобок мы переходим не на следующую строку, а обратно в проверку условия.
|
||||||
|
|
||||||
Для примера, выведем на экран все числа в заданном промежутке, границы которого мы обозначим как \code{а} и \code{b}. Для этого нам необходимо их инициализировать, то есть объявить и задать начальные значения, и пока \code{а} меньше \code{b} заходить в тело цикла где мы будем выводить на экран и инкрементировать меньшее число, пока оно не станет равным второму. Как только числа сравняются условие входа в тело цикла перестанет быть истинным, и мы не зайдём в него, оказавшись на следующей после тела цикла строке.
|
Для примера, выведем на экран все числа в заданном промежутке, границы которого мы обозначим как \code{а} и \code{b}. Для этого нам необходимо их инициализировать, то есть объявить и задать начальные значения, и пока \code{а} меньше \code{b} заходить в тело цикла где мы будем выводить на экран и инкрементировать меньшее число, пока оно не станет равным второму. Как только числа сравняются условие входа в тело цикла перестанет быть истинным, и мы не зайдём в него, оказавшись на следующей после тела цикла строке.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 10;
|
\begin{figure}[h!]
|
||||||
int b = 20;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
while (a < b) {
|
int a = 10;
|
||||||
printf("%d ", a++);
|
int b = 20;
|
||||||
}
|
while (a < b) {
|
||||||
\end{lstlisting}
|
printf("%d ", a++);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Запустим программу и убедимся что все работает:
|
Запустим программу и убедимся что все работает:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
10 11 12 13 14 15 16 17 18 19
|
10 11 12 13 14 15 16 17 18 19
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
В данном коде мы использовали оператор инкремента в постфиксной записи, которая означает, что значение переменной \code{а} сначала будет передано функции вывода на экран, а уже потом увеличено на единицу.
|
В данном коде мы использовали оператор инкремента в постфиксной записи, которая означает, что значение переменной \code{а} сначала будет передано функции вывода на экран, а уже потом увеличено на единицу.
|
||||||
\frm{Один проход тела цикла и возврат в управляющую конструкцию называется \textbf{итерацией}, этот термин можно очень часто услышать в беседах программистов, а зачастую и не только в беседах о программировании.}
|
\frm{Один проход тела цикла и возврат в управляющую конструкцию называется \textbf{итерацией}, этот термин можно очень часто услышать в беседах программистов, а зачастую и не только в беседах о программировании.}
|
||||||
Также допустимо использовать префиксную запись оператора инкремента, при которой, как не сложно догадаться, значение сначала будет увеличено, а уже потом передано функции вывода на экран.
|
Также допустимо использовать префиксную запись оператора инкремента, при которой, как не сложно догадаться, значение сначала будет увеличено, а уже потом передано функции вывода на экран.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int a = 10;
|
\begin{figure}[h!]
|
||||||
int b = 20;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
while (a < b) {
|
int a = 10;
|
||||||
printf("%d ", ++a);
|
int b = 20;
|
||||||
}
|
while (a < b) {
|
||||||
\end{lstlisting}
|
printf("%d ", ++a);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Запустим снова, чтобы увидеть разницу: после запуска первого варианта мы увидели в терминале значения от 10 до 19, а после запуска второго варианта значения от 11 до 20.
|
Запустим снова, чтобы увидеть разницу: после запуска первого варианта мы увидели в терминале значения от 10 до 19, а после запуска второго варианта значения от 11 до 20.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
11 12 13 14 15 16 17 18 19 20
|
11 12 13 14 15 16 17 18 19 20
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
Давайте напишем ещё один пример, в котором подсчитаем сколько чётных чисел на промежутке от \code{а} до \code{b}. для этого нам понадобится циклически пройтись по всем числам от \code{а} до \code{b} и в каждой итерации цикла, то есть для каждого числа, сделать проверку, является ли число чётным. Если является - увеличить счётчик чётных чисел для заданного промежутка. После того, как перестанет выполняться условие в скобках и наш цикл будет завершён, выведем в консоль количество чётных чисел.
|
\end{figure}
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
Давайте напишем ещё один пример, в котором подсчитаем сколько чётных чисел в промежутке от \code{а} до \code{b}. Для этого нам понадобится циклически пройтись по всем числам от \code{а} до \code{b} и в каждой итерации цикла, то есть для каждого числа, сделать проверку, является ли число чётным. Если является, увеличить счётчик чётных чисел для заданного промежутка. Обратите внимание, что приращение значения переменной \code{a} происходит прямо в проверке условия. Обычно такой код считается трудночитаемым, поскольку сложно сразу обратить внимание на то, что в пятой строке происходит не только сравнение, но и пост-инкремент переменной \code{a}.
|
||||||
int a = 10;
|
|
||||||
int b = 20;
|
\begin{figure}[h!]
|
||||||
int evens = 0;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
while (a < b) {
|
int a = 10;
|
||||||
if (a % 2 == 0)
|
int b = 20;
|
||||||
evens++;
|
int evens = 0;
|
||||||
a++;
|
while (a < b) {
|
||||||
}
|
if (a++ % 2 == 0)
|
||||||
printf("There are %d even numbers in sequence", evens);
|
evens++;
|
||||||
\end{lstlisting}
|
}
|
||||||
Запустим нашу программу и убедимся в правильности ее работы.
|
printf("There are %d even numbers in sequence", evens);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
После того, как перестанет выполняться условие в скобках, и наш цикл будет завершён, выведем в консоль получившееся количество чётных чисел. Запустим нашу программу и убедимся в правильности ее работы.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
There are 5 even numbers in sequence
|
There are 5 even numbers in sequence
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Программа выдала результат: пять чётных чисел, давайте пересчитаем вручную: 10, 12, 14, 16, 18, значение 20 не войдёт в результат работы программы, поскольку на последней итерации, когда \code{а = 20} условие в круглых скобках окажется ложным: двадцать не меньше двадцати. А раз условие не выполнено мы не попадаем в тело цикла.
|
Программа выдала результат: пять чётных чисел, давайте пересчитаем вручную: 10, 12, 14, 16, 18, значение 20 не войдёт в результат работы программы, поскольку на последней итерации, когда \code{а = 20} условие в круглых скобках окажется ложным: двадцать не меньше двадцати. А раз условие не выполнено мы не попадаем в тело цикла.
|
||||||
|
|
||||||
Цикл \code{while()} используется когда мы не можем достоверно сказать сколько итераций нам понадобится выполнить. На этом примере мы видим что тело цикла может быть любой сложности, оно может содержать как условные операторы так и другие циклы.
|
Цикл \code{while()} используется когда мы не можем достоверно сказать сколько итераций нам понадобится выполнить. На этом примере мы видим что тело цикла может быть любой сложности, оно может содержать как условные операторы так и другие циклы.
|
||||||
|
@ -61,17 +78,22 @@ $
|
||||||
Простой цикл \code{while()}, по очевидным причинам, попадает в категорию циклов с предусловием. Сначала программа проверяет условие, затем, по результатам этой проверки, либо выполняет либо не выполняет тело цикла. А раз есть циклы с предусловием, то логично предположить наличие циклов с постусловием. Как ни удивительно, в языке С есть и такой. Это разновидность цикла \code{while()}, который записывается как ключевое слово \code{do \{тело цикла\} while (условие);}, в этой конструкции сначала гарантированно один раз выполнится тело цикла, и только потом будет принято решение, нужно ли выполнять его снова. Такие циклы широко применяются для проверки пользовательского ввода до получения приемлемого результата и для ожидания ответов на запросы, что логично, поскольку глупо было бы ожидать ответ, не послав запрос.
|
Простой цикл \code{while()}, по очевидным причинам, попадает в категорию циклов с предусловием. Сначала программа проверяет условие, затем, по результатам этой проверки, либо выполняет либо не выполняет тело цикла. А раз есть циклы с предусловием, то логично предположить наличие циклов с постусловием. Как ни удивительно, в языке С есть и такой. Это разновидность цикла \code{while()}, который записывается как ключевое слово \code{do \{тело цикла\} while (условие);}, в этой конструкции сначала гарантированно один раз выполнится тело цикла, и только потом будет принято решение, нужно ли выполнять его снова. Такие циклы широко применяются для проверки пользовательского ввода до получения приемлемого результата и для ожидания ответов на запросы, что логично, поскольку глупо было бы ожидать ответ, не послав запрос.
|
||||||
|
|
||||||
Например, мы пишем калькулятор, и знаем, что в арифметике числа нельзя делить на ноль. Предположим, что наш пользователь этого не знает. Что ж, будем предлагать пользователю ввести делитель, пока он не введёт что-то отличное от нуля. Если пользовательский ввод равен нулю, значит нам нужно спросить его снова. Это и будет условием для очередной итерации цикла. Когда пользователь введёт удовлетворяющее нашему условию число произведём необходимые подсчёты и выведем их результаты в консоль.
|
Например, мы пишем калькулятор, и знаем, что в арифметике числа нельзя делить на ноль. Предположим, что наш пользователь этого не знает. Что ж, будем предлагать пользователю ввести делитель, пока он не введёт что-то отличное от нуля. Если пользовательский ввод равен нулю, значит нам нужно спросить его снова. Это и будет условием для очередной итерации цикла. Когда пользователь введёт удовлетворяющее нашему условию число произведём необходимые подсчёты и выведем их результаты в консоль.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int input;
|
|
||||||
do {
|
|
||||||
printf("Enter a divider for 100, ");
|
|
||||||
printf("remember, you can't divide by zero: ");
|
|
||||||
scanf("%d", &input);
|
|
||||||
} while (input == 0);
|
|
||||||
|
|
||||||
printf("100 / %d = %d", input, 100 / input);
|
\begin{figure}[h!]
|
||||||
\end{lstlisting}
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
int input;
|
||||||
|
do {
|
||||||
|
printf("Enter a divider for 100, ");
|
||||||
|
printf("remember, you can't divide by zero: ");
|
||||||
|
scanf("%d", &input);
|
||||||
|
} while (input == 0);
|
||||||
|
|
||||||
|
printf("100 / %d = %d", input, 100 / input);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Запустим программу. Каждый раз когда мы вводим ноль, программа будет повторно задавать нам вопрос.
|
Запустим программу. Каждый раз когда мы вводим ноль, программа будет повторно задавать нам вопрос.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Enter a divider for 100, remember, you can't divide by zero: 0
|
Enter a divider for 100, remember, you can't divide by zero: 0
|
||||||
|
@ -80,6 +102,7 @@ Enter a divider for 100, remember, you can't divide by zero: 5
|
||||||
100 / 5 = 20
|
100 / 5 = 20
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
Когда введем любое другое число получим результат нашего деления.
|
Когда введем любое другое число получим результат нашего деления.
|
||||||
\subsubsection{\code{for(;;)}}
|
\subsubsection{\code{for(;;)}}
|
||||||
Зачастую, складываются ситуации, когда мы точно знаем, сколько итераций цикла нам понадобится, например, когда мы последовательно проверяем содержимое созданного нами числового ряда, или заполняем значениями таблицы, границы которых заранее известны. В конце концов, для подсчёта среднего арифметического некоторого конечного числового ряда. В этих ситуациях принято использовать. Это цикл с предусловием, где заранее отведено место для инициализации переменной-счётчика, условия захода в следующую итерацию цикла и изменения переменной счетчика. В более поздних версиях С (C99) появилась возможность объявлять переменную счетчик прямо в управляющей конструкции.
|
Зачастую, складываются ситуации, когда мы точно знаем, сколько итераций цикла нам понадобится, например, когда мы последовательно проверяем содержимое созданного нами числового ряда, или заполняем значениями таблицы, границы которых заранее известны. В конце концов, для подсчёта среднего арифметического некоторого конечного числового ряда. В этих ситуациях принято использовать. Это цикл с предусловием, где заранее отведено место для инициализации переменной-счётчика, условия захода в следующую итерацию цикла и изменения переменной счетчика. В более поздних версиях С (C99) появилась возможность объявлять переменную счетчик прямо в управляющей конструкции.
|
||||||
|
@ -88,30 +111,36 @@ $
|
||||||
\centering\code{gcc program.c -std=c99 -o program}}
|
\centering\code{gcc program.c -std=c99 -o program}}
|
||||||
В классическом С необходимо объявить переменную счетчик заранее, а в управляющей конструкции можно только задать ей начальное значение.
|
В классическом С необходимо объявить переменную счетчик заранее, а в управляющей конструкции можно только задать ей начальное значение.
|
||||||
Условие захода в следующую итерацию цикла это логическое выражение, которое может принимать два значения: истина и ложь. Если условие истинно - идём на следующую итерацию (исполняем тело цикла), если ложно – выходим из цикла и перемещаемся на следующий после цикла оператор.
|
Условие захода в следующую итерацию цикла это логическое выражение, которое может принимать два значения: истина и ложь. Если условие истинно - идём на следующую итерацию (исполняем тело цикла), если ложно – выходим из цикла и перемещаемся на следующий после цикла оператор.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
// classic style
|
|
||||||
int i;
|
|
||||||
for (i = 0; /*condition*/; /*increment*/) {
|
|
||||||
// body
|
|
||||||
}
|
|
||||||
|
|
||||||
// C99 and later
|
\begin{figure}[h!]
|
||||||
for(int i = 0; /*condition*/; /*increment*/) {
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
// body
|
// classic style
|
||||||
}
|
int i;
|
||||||
|
for (i = 0; /*condition*/; /*increment*/) {
|
||||||
|
// body
|
||||||
|
}
|
||||||
|
|
||||||
// Example:
|
// C99 and later
|
||||||
int i;
|
for(int i = 0; /*condition*/; /*increment*/) {
|
||||||
for (i = 0; i < 5; i++) {
|
// body
|
||||||
printf("%d ", i);
|
}
|
||||||
}
|
|
||||||
\end{lstlisting}
|
// Example:
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
printf("%d ", i);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Цикл из примера выше выведет на экран числа от нуля до четырёх. На каждой итерации мы будем инкрементировать значение \code{i}, соответственно, пока логическое условие \code{i < 5} верно, мы будем заходить в тело цикла, а как только \code{i} станет равным пяти, логическое условие вернет ложь (\code{0}) и мы из него выйдем.
|
Цикл из примера выше выведет на экран числа от нуля до четырёх. На каждой итерации мы будем инкрементировать значение \code{i}, соответственно, пока логическое условие \code{i < 5} верно, мы будем заходить в тело цикла, а как только \code{i} станет равным пяти, логическое условие вернет ложь (\code{0}) и мы из него выйдем.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
0 1 2 3 4
|
0 1 2 3 4
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\subsection{Управление циклами}
|
\subsection{Управление циклами}
|
||||||
Операторы, которые осуществляют управление циклами называют операторами безусловного перехода, поскольку они просто перемещают выполнение программы на другую строку, невзирая ни на что. Программист должен сам описать логику такого перемещения, используя условные операторы.
|
Операторы, которые осуществляют управление циклами называют операторами безусловного перехода, поскольку они просто перемещают выполнение программы на другую строку, невзирая ни на что. Программист должен сам описать логику такого перемещения, используя условные операторы.
|
||||||
\paragraph{Оператор \code{continue;}}
|
\paragraph{Оператор \code{continue;}}
|
||||||
|
@ -123,29 +152,43 @@ $
|
||||||
Решим немного более сложную, чем выведение в консоль числовых рядов задачу возведения числа в степень. Язык С не предоставляет оператора возведения в степень по умолчанию, как это делают некоторые другие языки высокого уровня, такие как Python, поэтому для этой математической операции нам нужно подключать специальную математическую библиотеку. Но иногда это может оказаться излишним, ведь не так сложно написать собственную функцию, которая бы делала это.
|
Решим немного более сложную, чем выведение в консоль числовых рядов задачу возведения числа в степень. Язык С не предоставляет оператора возведения в степень по умолчанию, как это делают некоторые другие языки высокого уровня, такие как Python, поэтому для этой математической операции нам нужно подключать специальную математическую библиотеку. Но иногда это может оказаться излишним, ведь не так сложно написать собственную функцию, которая бы делала это.
|
||||||
\frm{Как известно, возведение в степень - это последовательное умножение основания на само себя указанное количество раз.}
|
\frm{Как известно, возведение в степень - это последовательное умножение основания на само себя указанное количество раз.}
|
||||||
А раз заранее известно, сколько раз мы будем умножать основание само на себя, это работа для цикла \code{for(;;)}. Объявим переменную-итератор \code{i}, переменную-основание, переменную-показатель и переменную, в которую будем сохранять промежуточные и конечный результаты.
|
А раз заранее известно, сколько раз мы будем умножать основание само на себя, это работа для цикла \code{for(;;)}. Объявим переменную-итератор \code{i}, переменную-основание, переменную-показатель и переменную, в которую будем сохранять промежуточные и конечный результаты.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int i;
|
\begin{figure}[h!]
|
||||||
int base;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
int significative;
|
int i;
|
||||||
int result = 1;
|
int base;
|
||||||
\end{lstlisting}
|
int significative;
|
||||||
Логика работы следующая: результатом работы будет совокупность результатов предыдущих итераций умноженных на основание.
|
int result = 1;
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
\end{lstlisting}
|
||||||
for (i = 0; i < significative; i++) {
|
\end{figure}
|
||||||
result = result * base;
|
Принцип возведения числа в степень следующий: результатом будет совокупность результатов предыдущих итераций умноженных на основание. А значит и алгоритм будет повторяться столько раз, сколько указано в показателе, на каждом повторении умножая промежуточный результат на основание, сохраняя произведение обратно в переменную с промежуточным результатом.
|
||||||
}
|
|
||||||
\end{lstlisting}
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
for (i = 0; i < significative; i++) {
|
||||||
|
result = result * base;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Запись вида \code{result = result * base;} можно сократить до \code{result *= base;}, и выведем результаты работы цикла в консоль.
|
Запись вида \code{result = result * base;} можно сократить до \code{result *= base;}, и выведем результаты работы цикла в консоль.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
for (i = 0; i < significative; i++) {
|
\begin{figure}[h!]
|
||||||
result *= base;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
}
|
for (i = 0; i < significative; i++) {
|
||||||
printf("%d powered by %d is %d \n", base, significative, result);
|
result *= base;
|
||||||
\end{lstlisting}
|
}
|
||||||
|
printf("%d powered by %d is %d \n", base, significative, result);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Конечно, мы можем спросить у пользователя какое число, и в какую степень он хочет возвести, для этого применим уже привычные нам конструкции. Так, весь код программы будет иметь следующий вид:
|
Конечно, мы можем спросить у пользователя какое число, и в какую степень он хочет возвести, для этого применим уже привычные нам конструкции. Так, весь код программы будет иметь следующий вид:
|
||||||
\lstinputlisting[language=C,style=CCodeStyle]{../sources/power.c}
|
|
||||||
\label{code:power}
|
\begin{figure}[h!]
|
||||||
Запустим нашу программу, введем для базы значение два, для показателя десять. Убедимся, что наша программа работает корректно, $2^{10}=1024$.
|
\lstinputlisting[language=C,style=CCodeStyle]{../sources/power.c}
|
||||||
|
\label{code:power}
|
||||||
|
\end{figure}
|
||||||
|
Запустим нашу программу, введем для базы значение два, для показателя десять. Убедимся, что наша программа работает корректно, $2^{10}=1024$. Естественно, что существуют более оптимальные алгоритмы возведения в степень, например, возведение в степень с использованием свойства чётности степени, $2^{10}=4^{5}$, то есть при чётном показателе можно возвести основание в квадрат, а показатель поделить на два, тем самым сократив количество итераций простого цикла, описанного выше, вдвое.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Enter base: 2
|
Enter base: 2
|
||||||
|
@ -153,41 +196,57 @@ Enter significative: 10
|
||||||
2 powered by 10 is 1024
|
2 powered by 10 is 1024
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
Вообще, степени двойки, пожалуй, самые популярные в программировании числа.
|
\end{figure}
|
||||||
\paragraph{Простое число}
|
\paragraph{Простое число}
|
||||||
Решим ещё одну несложную задачу. Напишем программу, которая будет определять, является ли введённое пользователем число простым. Мы напишем не самый быстрый и оптимальный алгоритм, но постараемся использовать все доступные нам конструкции. То есть, эта задача призвана продемонстрировать возможности языка, а не улучшить или заменить существующие, более оптимальные алгоритмы проверки.
|
Решим ещё одну несложную задачу. Напишем программу, которая будет определять, является ли введённое пользователем число простым. Мы напишем не самый быстрый и оптимальный алгоритм, но постараемся использовать все доступные нам конструкции. То есть, эта задача призвана продемонстрировать возможности языка, а не улучшить или заменить существующие, более оптимальные алгоритмы проверки.
|
||||||
\frm{Что такое простое число? Это такое число, которое имеет ровно два делителя с целочисленным результатом - единицу и само себя.}
|
\frm{Что такое простое число? Это такое число, которое имеет ровно два делителя с целочисленным результатом - единицу и само себя.}
|
||||||
Наша программа будет запрашивать у пользователя число и определять, простое оно или нет. Для этого заведём переменную, и привычными нам функциями, попросим пользователя ввести число, которое положим в неё. Для подсчетов нам понадобятся дополнительные переменные, например, переменная которая будет хранить количество делителей, назовем ее \code{dividers} и переменная итератор \code{i} значение которой будет увеличиваться от единицы до введенного пользователем числа.
|
Наша программа будет запрашивать у пользователя число и определять, простое оно или нет. Для этого заведём переменную, и привычными нам функциями, попросим пользователя ввести число, которое положим в неё. Для подсчетов нам понадобятся дополнительные переменные, например, переменная которая будет хранить количество делителей, назовем ее \code{dividers} и переменная итератор \code{i} значение которой будет увеличиваться от единицы до введенного пользователем числа.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
int number;
|
\begin{figure}[h!]
|
||||||
int dividers = 0, i = 1;
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("Enter number: ");
|
int number;
|
||||||
scanf("%d", &number);
|
int dividers = 0, i = 1;
|
||||||
\end{lstlisting}
|
printf("Enter number: ");
|
||||||
|
scanf("%d", &number);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Поскольку мы не знаем, сколько итераций понадобится, напишем цикл \code{while}, и пройдемся от единицы, которую мы записали в переменную \code{i} до введённого пользователем числа. После того как мы переберем все возможные варианты от единицы до введённого пользователем числа выведем в консоль получившийся результат. Напишем введённое число, а дальше предоставим программе выбор ставить ли частицу <<не>> при помощи заполнителя \code{\%s} и тернарного оператора. В случае истинности условия \code{dividers == 2} тернарный оператор вернет пустую строку, в случае ложности, вернет частицу <<не>>. Обратите внимание на то, как оператор вывода на экран написан в несколько срок. Такие разделения на несколько строк можно часто увидеть, если оператор длинный и может, например, выйти за пределы экрана.
|
Поскольку мы не знаем, сколько итераций понадобится, напишем цикл \code{while}, и пройдемся от единицы, которую мы записали в переменную \code{i} до введённого пользователем числа. После того как мы переберем все возможные варианты от единицы до введённого пользователем числа выведем в консоль получившийся результат. Напишем введённое число, а дальше предоставим программе выбор ставить ли частицу <<не>> при помощи заполнителя \code{\%s} и тернарного оператора. В случае истинности условия \code{dividers == 2} тернарный оператор вернет пустую строку, в случае ложности, вернет частицу <<не>>. Обратите внимание на то, как оператор вывода на экран написан в несколько срок. Такие разделения на несколько строк можно часто увидеть, если оператор длинный и может, например, выйти за пределы экрана.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
while (i <= number) {
|
\begin{figure}[h!]
|
||||||
// here will be the check algorithm
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
}
|
while (i <= number) {
|
||||||
printf("Number %d is%s prime",
|
// here will be the check algorithm
|
||||||
number,
|
}
|
||||||
(dividers == 2) ? "" : " not"
|
printf("Number %d is%s prime",
|
||||||
);
|
number,
|
||||||
|
(dividers == 2) ? "" : " not"
|
||||||
|
);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
|
Итак, что будет происходить на каждой итерации цикла? Мы будем проверять делится ли введённое пользователем число на текущее значение счётной переменной. Если остаток от деления исходного числа на текущий делитель равен нулю, то мы увеличим счетчик количества целочисленных делителей для данного числа.
|
||||||
|
\frm{Снова обратите внимание, что переменная-итератор, последовательно перебирающая делители, увеличивается прямо в круглых скобках оператора \code{if}. Таким образом автор хочет подчеркнуть, что в коде, который пишут живые люди, такие конструкции встречаются чаще, чем хотелось бы. Часто даже можно встретить инкремент счётчика прямо в проверке условия захода в тело цикла и это ещё сильнее влияет как на читаемость кода, так и на его понятность.}
|
||||||
|
Тело цикла для нашей задачи примет следующий вид:
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
if (number % i++ == 0) {
|
||||||
|
dividers++;
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (dividers == 3)
|
||||||
|
break;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
Итак, что будет происходить на каждой итерации цикла? Мы будем проверять делится ли введённое пользователем число на текущее значение итератора. Если остаток от деления исходного числа на текущий делитель равен нулю, то мы увеличим счетчик количества целочисленных делителей для данного числа. Тело цикла примет следующий вид:
|
\end{figure}
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
Если количество целочисленных делителей не изменилось, то мы прекратим текущую итерацию цикла при помощи ключевого слова \code{continue}. Как мы знаем, оператор \code{continue} передаст управление в логическую конструкцию цикла, заставив программу проигнорировать все дальнейшие инструкции в рамках текущей итерации. Если количество целочисленных делителей достигнет трёх, что будет означать нецелесообразность дальнейших вычислений, мы разорвем цикл при помощи ключевого слова \code{break}. Напомним, что операторы \code{break;} и \code{continue;} являются операторами безусловного перехода, а значит достигнув их программа передаст управление соответствующей строке безусловно и без предупреждений. И полный получившийся код приложения будет такой:
|
||||||
if (number++ % i == 0) {
|
|
||||||
dividers++;
|
\begin{figure}[h!]
|
||||||
} else {
|
\lstinputlisting[language=C,style=CCodeStyle]{../sources/prime.c}
|
||||||
continue;
|
\label{code:isPrime}
|
||||||
}
|
\end{figure}
|
||||||
if (dividers == 3)
|
|
||||||
break;
|
|
||||||
\end{lstlisting}
|
|
||||||
Если количество целочисленных делителей не изменилось, то мы прекратим текущую итерацию цикла при помощи ключевого слова \code{continue}. Как мы знаем, оператор \code{continue} передаст управление в логическую конструкцию цикла, заставив программу проигнорировать все дальнейшие инструкции в рамках текущей итерации. Если количество целочисленных делителей достигнет трёх, что будет означать нецелесообразность дальнейших вычислений, мы разорвем цикл при помощи ключевого слова \code{break}. И полный получившийся код приложения будет такой:
|
|
||||||
\lstinputlisting[language=C,style=CCodeStyle]{../sources/prime.c}
|
|
||||||
\label{code:isPrime}
|
|
||||||
Естественно, повторимся, этот код можно оптимизировать по множеству направлений, как минимум, сократив как количество проверок, так и границы проверок (нет смысла проверять числа больше, чем $\sqrt{number}$). Дополнительно можно не проверять чётные числа, например.
|
Естественно, повторимся, этот код можно оптимизировать по множеству направлений, как минимум, сократив как количество проверок, так и границы проверок (нет смысла проверять числа больше, чем $\sqrt{number}$). Дополнительно можно не проверять чётные числа, например.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Enter number: 2
|
Enter number: 2
|
||||||
|
@ -203,6 +262,7 @@ Enter number: 1457
|
||||||
Number 1457 is not prime
|
Number 1457 is not prime
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
\end{figure}
|
||||||
\subsection{Множественный выбор \code{switch()\{\}}}
|
\subsection{Множественный выбор \code{switch()\{\}}}
|
||||||
Пришло время поговорить об операторе множественного выбора \code{switch()\{\}}. Тем более, что теперь мы обладаем всеми необходимыми для этого знаниями. Оператор множественного выбора используется когда мы хотим описать действия для какого-то ограниченного количества условий. В отличие от оператора \code{if()}, который может использоваться также и для проверки диапазонов значений.
|
Пришло время поговорить об операторе множественного выбора \code{switch()\{\}}. Тем более, что теперь мы обладаем всеми необходимыми для этого знаниями. Оператор множественного выбора используется когда мы хотим описать действия для какого-то ограниченного количества условий. В отличие от оператора \code{if()}, который может использоваться также и для проверки диапазонов значений.
|
||||||
\frm{Это не совсем точно, потому что \code{switch()\{\}} в языке C тоже может использоваться для проверки диапазонов значений, но это довольно редко применяется, а в С++ и других языках может вовсе не работать. Не стоит пугаться, увидев \code{case 5 ... 50:} это как раз проверка диапазона целочисленных значений от 5 до 50 включительно.}
|
\frm{Это не совсем точно, потому что \code{switch()\{\}} в языке C тоже может использоваться для проверки диапазонов значений, но это довольно редко применяется, а в С++ и других языках может вовсе не работать. Не стоит пугаться, увидев \code{case 5 ... 50:} это как раз проверка диапазона целочисленных значений от 5 до 50 включительно.}
|
||||||
|
@ -216,47 +276,55 @@ $
|
||||||
\item 4 – деление.
|
\item 4 – деление.
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
Здесь вступает в силу тот факт, что на ноль делить нельзя, поэтому нам нужно не дать пользователю ввести в качестве второго операнда \code{"0"}, если в качестве оператора он выбрал деление. Конечно, это можно оставить на усмотрение пользователя, но мы, как сознательные программисты, не дадим пользователю в арифметическом порыве сломать нашу программу. Совершенно очевидно, что просто скопировать ввод будет неправильным действием. Для того чтобы не дать пользователю сделать неправильный ввод введем в прорамму условие: если пользователь выбрал деление, используя цикл \code{do \{\} while();} будем просить его ввести второй операнд отличный от нуля, а если выбранный оператор не является делением, то просто попросим пользователя ввести второе число, без проверок и повторений.
|
Здесь вступает в силу тот факт, что на ноль делить нельзя, поэтому нам нужно не дать пользователю ввести в качестве второго операнда \code{"0"}, если в качестве оператора он выбрал деление. Конечно, это можно оставить на усмотрение пользователя, но мы, как сознательные программисты, не дадим пользователю в арифметическом порыве сломать нашу программу. Совершенно очевидно, что просто скопировать ввод будет неправильным действием. Для того чтобы не дать пользователю сделать неправильный ввод введем в прорамму условие: если пользователь выбрал деление, используя цикл \code{do \{\} while();} будем просить его ввести второй операнд отличный от нуля, а если выбранный оператор не является делением, то просто попросим пользователя ввести второе число, без проверок и повторений.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
if (operator == 4) {
|
\begin{figure}[h!]
|
||||||
do {
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
printf("/nEnter second operand: ");
|
if (operator == 4) {
|
||||||
scanf("%f", &second);
|
do {
|
||||||
} while (second == 0);
|
printf("/nEnter second operand: ");
|
||||||
} else {
|
scanf("%f", &second);
|
||||||
printf("/nEnter second operand: ");
|
} while (second == 0);
|
||||||
scanf("%f", &second);
|
} else {
|
||||||
}
|
printf("/nEnter second operand: ");
|
||||||
\end{lstlisting}
|
scanf("%f", &second);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Если мы воспользуемся нашими знаниями на текущий момент, то мы напишем примерно следующее: если оператор равен единице, то делать это, в противном случае, если оператор равен двум, то делать вот это, и так далее, описывали бы все возможные действия условными операторами. Получился бы весьма громоздкий код.
|
Если мы воспользуемся нашими знаниями на текущий момент, то мы напишем примерно следующее: если оператор равен единице, то делать это, в противном случае, если оператор равен двум, то делать вот это, и так далее, описывали бы все возможные действия условными операторами. Получился бы весьма громоздкий код.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
if (operator == 1) {
|
\begin{figure}[h!]
|
||||||
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
|
if (operator == 1) {
|
||||||
|
//...
|
||||||
|
} else if (operator == 2) {
|
||||||
|
//...
|
||||||
|
}
|
||||||
//...
|
//...
|
||||||
} else if (operator == 2) {
|
\end{lstlisting}
|
||||||
//...
|
\end{figure}
|
||||||
}
|
|
||||||
//...
|
|
||||||
\end{lstlisting}
|
|
||||||
Но хорошая новость в том, что существует гораздо более удобный оператор \code{switch()\{\}}. Воспользуемся им относительно переменной \code{operator}. Разделим действия оператора \code{switch()\{\}} на несколько так называемых \textit{кейсов}.
|
Но хорошая новость в том, что существует гораздо более удобный оператор \code{switch()\{\}}. Воспользуемся им относительно переменной \code{operator}. Разделим действия оператора \code{switch()\{\}} на несколько так называемых \textit{кейсов}.
|
||||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
|
||||||
switch (operator) {
|
\begin{figure}[h!]
|
||||||
case 1:
|
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||||
result = first + second;
|
switch (operator) {
|
||||||
case 2:
|
case 1:
|
||||||
result = first - second;
|
result = first + second;
|
||||||
case 3:
|
case 2:
|
||||||
result = first * second;
|
result = first - second;
|
||||||
case 4:
|
case 3:
|
||||||
result = first / second;
|
result = first * second;
|
||||||
default:
|
case 4:
|
||||||
printf("Unknown operator\n");
|
result = first / second;
|
||||||
}
|
default:
|
||||||
\end{lstlisting}
|
printf("Unknown operator\n");
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{figure}
|
||||||
Оператор \code{switch()\{\}} последовательно проверит входящую переменную на соответствие описанным в кейсах значениях. В случае, если значение совпадёт, будет выполнен блок кода до оператора \code{break;}, если же значение переменной не совпадёт ни с одним из описанный в кейсах, выполнится блок по умолчанию \code{default}.
|
Оператор \code{switch()\{\}} последовательно проверит входящую переменную на соответствие описанным в кейсах значениях. В случае, если значение совпадёт, будет выполнен блок кода до оператора \code{break;}, если же значение переменной не совпадёт ни с одним из описанный в кейсах, выполнится блок по умолчанию \code{default}.
|
||||||
\frm{Важно помнить, что в случае отсутствия внутри \code{case} оператора \code{break;}, программа будет выполнять последующие кейсы, пока не найдёт \code{break;} или пока не закончится конструкция \code{switch()\{\}}, то есть пока не встретится её закрывающая фигурная скобка.}
|
\frm{Важно помнить, что в случае отсутствия внутри \code{case} оператора \code{break;}, программа будет выполнять последующие кейсы, пока не найдёт \code{break;} или пока не закончится конструкция \code{switch()\{\}}, то есть пока не встретится её закрывающая фигурная скобка.}
|
||||||
В кейсах мы опишем присваивание результата в переменную \code{result}, а после выхода из \code{switch()\{\}} - вывод результата в консоль. Кейсом по умолчанию будет вывод пользователю сообщения о невозможности распознать оператор. Так получается, что даже если мы ввели неизвестный оператор, программа попытается вывести в консоль результат, что неприемлемо. Поэтому кейс по умолчанию должен содержать также и оператор \code{return 1;} вынуждающий программу экстренно завершиться с кодом ошибки \code{1}. Получится такой код:
|
В кейсах мы опишем присваивание результата в переменную \code{result}, а после выхода из \code{switch()\{\}} - вывод результата в консоль. Кейсом по умолчанию будет вывод пользователю сообщения о невозможности распознать оператор. Так получается, что даже если мы ввели неизвестный оператор, программа попытается вывести в консоль результат, что неприемлемо. Поэтому кейс по умолчанию должен содержать также и оператор \code{return 1;} вынуждающий программу экстренно завершиться с кодом ошибки \code{1}. Запустив описанный нами калькулятор, убедимся что все работает. Сымитируем нерадивого пользователя и несколько раз попробуем ввести при использовании четвёртого оператора цифру ноль, программа естественно не даст нам этого сделать.
|
||||||
\lstinputlisting[language=C,style=CCodeStyle]{../sources/calculator.c}
|
|
||||||
\label{code:calculator}
|
\begin{figure}[h!]
|
||||||
Запустив описанный нами калькулятор, убедимся что все работает. Сымитируем нерадивого пользователя и несколько раз попробуем ввести при использовании четвёртого оператора цифру ноль, программа естественно не даст нам этого сделать.
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
$ ./program
|
$ ./program
|
||||||
Enter first operand: 10
|
Enter first operand: 10
|
||||||
|
@ -272,4 +340,11 @@ Enter 1 for (+), 2 for (-), 3 for (*), 4 for (/): 5
|
||||||
Unknown operator
|
Unknown operator
|
||||||
$
|
$
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
На основе этого кода можно описывать любые виды меню, описать поведение программ, которые должны опираться на получаемые извне команды или описывать конечные автоматы.
|
\end{figure}
|
||||||
|
Также приведём полный листинг получившейся программы, пока он ещё помещается на одну страницу. Далее некоторые примеры будет невозможно привести полностью, поэтому собирать их в единый работающий код читатель будет вынужден самостоятельно.
|
||||||
|
|
||||||
|
\begin{figure}[h!]
|
||||||
|
\lstinputlisting[language=C,style=CCodeStyle]{../sources/calculator.c}
|
||||||
|
\label{code:calculator}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ int main(int argc, char *argv[]) {
|
||||||
printf("Enter number: ");
|
printf("Enter number: ");
|
||||||
scanf("%d", &number);
|
scanf("%d", &number);
|
||||||
while (i <= number) {
|
while (i <= number) {
|
||||||
if (number++ % i == 0) {
|
if (number % i++ == 0) {
|
||||||
dividers++;
|
dividers++;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in New Issue