\section{Условия, блоки кода, видимость} \subsection{Условный оператор} \paragraph{\code{if()}} пожалуй, самый часто используемый в любом языке программирования, в том числе и в языке С оператор. Оператор \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); if (answer == 'y') { printf("Hello, user"); } \end{lstlisting} \paragraph{\code{else}} Зачастую складываются ситуации, когда нужно выполнить разные наборы действий, в зависимости от результата проверки условия. Для таких случаев используется дополнение к оператору \code{if()} - оператор \code{else}, в котором описывается последовательность действий выполняемая в случае если условие в круглых скобках дало ложный результат. Код немного изменится, не будем приводить повторяющиеся части, взаимодействия с пользователем, сконцентрируемся на условном операторе: \begin{lstlisting}[language=C,style=CCodeStyle] if (answer == 'y') { printf("Hello, user"); } else { printf("I didn't want to salute you anyway"); } \end{lstlisting} Как вы видите, в зависимости от того что ввел пользователь мы реализуем ту или иную ветку оператора \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} будет непонимание программы того что ввел пользователь. Выведем в консоль надпись <<Я не могу понять Ваш ввод>>. \begin{lstlisting}[language=C,style=CCodeStyle] if (answer == 'y') { printf("Hello, user"); } else if (answer == 'n') { printf("I didn't want to salute you anyway"); } else { printf("I can't understand your input"); } \end{lstlisting} Операторов \code{else if} в одном операторе выбора может быть сколько угодно, в отличие от оператора \code{if} и оператора \code{else} которых не может быть больше одного. \begin{verbatim} $ ./program do you want me to salute you (y/n)? y Hello, user $ ./program do you want me to salute you (y/n)? n I didn't want to salute you anyway $ ./program do you want me to salute you (y/n)? x I can't understand your input $ \end{verbatim} \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{lstlisting}[language=C,style=CCodeStyle] printf("%s", (1 > 0) ? "true" : "false"); \end{lstlisting} Проверим как это работает, и в результате видим true, потому что единица действительно больше нуля. \begin{verbatim} $ ./program true $ \end{verbatim} \paragraph{Вложенные условия и сокращения} Внутри фигурных скобок конструкций \code{if()} находится код программы, поэтому там могут находиться и другие условные операторы. Условия, расположенные таким образом называются вложенными. Никаких ограничений на использование вложенных условий в языке С нет. В примере ниже показано, что условия всегда выполняются (единица в круглых скобках будет означать, что условие всегда истинно), а комментариями с многоточием показано, где может располагаться код программы. \begin{lstlisting}[language=C,style=CCodeStyle] if (1) { // operators if (1) { // operators } // operators } \end{lstlisting} Также важно отметить, что если после условных операторов следует только один оператор языка, как в примере ниже, то использование фигурных скобок не обязательно, хотя и считается хорошим тоном писать их всегда: \begin{lstlisting}[language=C,style=CCodeStyle] // one operator condirion if (1) { // single operator } // also one operator condition if (1) // single operator // another one operator condition if (1) // single operator else // single operator \end{lstlisting} При использовании такой записи можно легко допустить ошибку: забыть о необходимости объединения кода фигурными скобками, и предполагать, что несколько операторов могут выполниться по условию без фигурных скобок. \frm{Часто это заблуждение приводит к трудноуловимым ошибкам в коде, когда программа компилируется, запускается, но работает не так, как следует.} \subsection{Операции сравнения} \paragraph{Арифметическое сравнение} это знакомые и привычные нам со школы операторы <<больше>> (\code{>}), <<меньше>> (\code{<}), <<больше или равно>> (\code{>=}), <<меньше или равно>> (\code{<=}), а также в языке С присутствуют в виде отдельных операторов <<проверка на равенство>>, которая записывается в виде двух знаков равенства (\code{==}), и <<проверка на неравенство>>, которая записывается в виде восклицательного знака и символа равенства (\code{!=}). Возвращают истину, когда выполняются соответствующие названиям условия и ложь, когда условия не выполняются, что очевидно. \paragraph{Логических операторов} три: это \code{И (\&\&)}, \code{ИЛИ (||)}, \code{НЕ (!)}. В отличие от арифметических двоичных операторов - логические возвращают истину или ложь т.е. в случае языка С - \code{1} либо \code{0} и работают с операндами слева и справа целиком, а не поразрядно. В этом легко убедиться, попытавшись вывести на экран результат сравнения с заведомо неверным форматированием и получив ошибку, говорящую о том, что компилятор ожидал от нас число, а мы хотим его отформатировать в строку. \begin{lstlisting}[language=C,style=CCodeStyle] printf("%s\n", 1 == 1); \end{lstlisting} Некоторые компиляторы выдают ошибку на этапе компиляции, некоторые компилируют такой код, но программа не сможет выполниться и выдаст ошибку \textbf{Segmentation fault}. Это зависит от большого количества факторов, таких как тип операционной системы, тип и версия компилятора, версия используемого стандарта языка. Отдельного внимания заслуживает применение оператора поразрядного (арифметического) \code{ИСКЛЮЧАЮЩЕГО ИЛИ} в качестве логического. В случае такого применения оператор \code{ИСКЛЮЧАЮЩЕГО ИЛИ}, фактически, дублирует сравнение на неравенство, это легко объяснить, проведя анализ происходящего с числами при таком сравнении: \begin{lstlisting}[language=C,style=CCodeStyle] int a = 11; //00001011 int b = 11; //00001011 // ^ 00000000 if (a ^ b) { printf("numbers are not equal\n"); } \end{lstlisting} Данный код внутри фигурных скобок оператора \code{if()} никогда не выполнится, поскольку в С оператор сравнения работает с числами, интерпретируя ноль как ложь, а любое ненулевое значение как истину, мы наблюдаем, что побитовое \code{ИСКЛЮЧАЮЩЕЕ ИЛИ} - это тоже самое, что проверка на неравенство. \begin{itemize} \item оператор \code{И (\&\&)} возвращает истину только когда оба операнда истинны; \begin{tabular}{|c|c|c|} \hline операнд & операнд & результат \\ \hline 0 & 0 & 0 \\ \hline 0 & 1 & 0 \\ \hline 1 & 0 & 0 \\ \hline 1 & 1 & 1 \\ \hline \end{tabular} \item оператор \code{ИЛИ (||)} возвращает истину когда хотя бы один из операндов истинный; \begin{tabular}{|c|c|c|} \hline операнд & операнд & результат \\ \hline 0 & 0 & 0 \\ \hline 0 & 1 & 1 \\ \hline 1 & 0 & 1 \\ \hline 1 & 1 & 1 \\ \hline \end{tabular} \item оператор \code{НЕ (!)} возвращает истину когда операнд ложный; \begin{tabular}{|c|c|} \hline операнд & результат \\ \hline 0 & 1 \\ \hline 1 & 0 \\ \hline \end{tabular} \end{itemize} Используя логические операторы в программе мы можем написать логику практически любой сложности. В языке С нет ограничений на использование сложных условий. Сложные условия это такие, где в круглых скобках выполняется более одного сравнения. Сравнения производятся в порядке заранее оговоренного приоритета. В списке ниже указаны операторы в порядке уменьшения их приоритета: \begin{enumerate} \item $!$ \item $<, <=, >, >=$ \item $==, !=$ \item $\&\&$ \item $||$ \end{enumerate} Приведём короткий пример: дана некоторая целочисленная переменная и нужно выяснить, не выходит ли эта переменная за рамки заданных значений, например от нуля до десяти. Условие можно будет скомбинировать так: \code{x >= 0 \&\& x <= 10}. В случае его истинности - выдадим сообщение о том, что \code{х} подходит. \begin{lstlisting}[language=C,style=CCodeStyle] int x = 7; if ((x >= 0) && (x <= 10)) { printf("X Fits!\n"); } \end{lstlisting} В данной записи мы видим, что сначала \code{х} сравнивается с нулём, затем с десятью, и в конце результаты будут сравнены между собой. \begin{verbatim} $ ./program X Fits! $ \end{verbatim} Самый не приоритетный оператор тернарный, внимательный читатель мог заметить, что он даже не вошёл в список выше, это сделано, поскольку использование тернарного оператора внутри условий нежелательно. Тернарный оператор внутри условий резко снижает читаемость кода и усложняет его интерпретацию. Если Вы сомневаетесь в приоритете сравнений или Вам необходимо описать какое-то очень сложное условие, всегда можно воспользоваться простыми математическими круглыми скобками, задав приоритет операций явно. В таком случае в первую очередь будут выполнены операции в скобках. \subsection{Блоки кода и область видимости} Говоря об операторах языка С и управляющих конструкциях нельзя не сказать о <<блоках кода>> и <<областях видимости>>. Как видно, условные операторы содержат записи в фигурных скобках. В такие же скобки заключён код функции \code{main}. Эти скобки называются <<операторными>>, а то что в них содержится, называется <<блоком кода>> или <<телом>> оператора или функции. Все переменные, которые инициализируются внутри блока кода, существуют и <<видны>> только внутри кодового блока. Поэтому пространство между операторными скобками также называют <<областью видимости>>. \begin{lstlisting}[language=C,style=CCodeStyle] int x = 7; if ((x >= 0) && (x <= 10)) { int var = 0; printf("X Fits!\n"); } printf("%d", var); \end{lstlisting} На этом примере можно увидеть, что мы не можем напечатать значение переменной \code{var}, поскольку она была создана внутри блока кода оператора \code{if()} и перестала существовать для нашей программы как только мы вышли за его пределы. Такой код даже не скомпилируется: \begin{verbatim} $ gcc -o program main.c error: 'var' undeclared (first use in this function); did you mean 'char'? printf("%d", var); ^~~ char $ \end{verbatim}