Существуют операторы преобразования данных и операторы управления работой программы.
Операции¶
Характеристики операций¶
Приоритет операций¶
Результат операций¶
В языке Си у операций могут быть следующие результаты:
Арифметические операции¶
Знак
Операция
*
Умножение
/
Деление и целочисленное деление нацело
%
Деление по модулю и остаток от деления
+
Сложение
—
Вычитание
Знак / всегда означает деление. Однако если с обеих сторон от этого знака стоят целые величины (константы, переменные или их комбинации), он означает целочисленное деление. Если в результате такого деления получается остаток, С++ его отбрасывает.
Знак % означает выделение остатка при целочисленном делении. Эта операция требует, чтобы с обеих сторон от ее знака стояли целые величины
Присваивание¶
Особенностью Си является возможность комбинирования операции присваивания с другими операциями, а также выполнение множественного присваивания.
Существуют два класса выражений:
l-value (левостороннее выражение. Может стоять слева от присваивания)
r-value (правостороннее выражение. Может стоять справа от присваивания)
Составное присваивание¶
Эта группа операций позволяет совместить арифметику и присваивание, что дает выразительность и удобочитаемость программам.
Преобразование типов¶
В различных выражениях могут встречаться данные как одного, так и разных типов. Компилятор может выполнять операцию приведения типов по-умолчанию, действуя согласно простого правила: короткие типы приводятся к длинным. Если необходимо изменить стандартное преобразование, то вводят явное приведение типов.
Сравнение¶
Операции сравнения являются бинарными и используются для сравнения двух значений
Значения операций¶
Пример выражения
Результат
-4+6
2
c=3+8
11
5>3
1
6+(c=3+8)
17
6+c=3+8
Ошибка!
Инкремент и декремент¶
будет эквивалентен следующему
будет эквивалентен следующему
Операции инкремента/декремента¶
Что можно сказать о следующей программе?
Ошибка компиляции:l-value required as left operand of assignment
Неопределенное поведение¶
Определение (см. Википедию)
Неопределённое поведение (англ. undefined behaviour) — свойство некоторых языков программирования (наиболее заметно в Си), программных библиотек и аппаратного обеспечения в определённых маргинальных ситуациях выдавать результат, зависящий от реализации компилятора (библиотеки, микросхемы) и случайных параметров
При его выполнении переменная i может принять значения 13 или 14 для C/C++, 13 для Java, PHP и C#, 12 при реализации на LISP.
Неопределенность в языке C/C++ связана с тем, что согласно стандартам С и С++ побочные эффекты (то есть инкремент в данном случае) могут быть применены в любой удобный для компилятора момент между двумя точками следования (см. раздел Дополнительная информация).
Операторы¶
Основные алгоритмические конструкции¶
Основная теорема структурного программирования
Программа для решения любой задачи может быть составлена из комбинации следования, ветвления и цикла (Бойм-Якопини, 1966).
Классификация операторов¶
Оператор if¶
Оператор if¶
Примеры if¶
Несколько версий одной и той же программы
Удачные/неудачные конструкции¶
Удачные и неудачные конструкции¶
Среди всех приведенных конструкций наиболее удачной следует признать последнюю, так как она лишена нагромождений и воспринимается однозначно.
Если необходимо сравнить значение переменной с набором констант, то лучше отказаться от if в пользу switch
Сложные выражения¶
С позволяет конструировать очень сложные выражения. Эта сложность должна быть оправдана.
Оператор switch¶
Оператор switch¶
Вопрос: Чему будут равны значения переменных, если строка: babah!?
Оператор while¶
Операторы цикла¶
Оператор while¶
Ошибки при организации while¶
В чем состоит ошибка?
Оператор do while¶
Оператор do while¶
Это цикл с постусловием. Тело цикла выполняется как минимум 1 раз
Оператор for¶
Оператор for¶
Оператор for¶
Операторы управления¶
Оператор goto усложняет отладку программы и сильно портит стиль разработчика (‘’спагетти-код’‘)!
Спагетти-код¶
Пример спагетти кода на языке BASIC:
Тоже самое, но без goto:
Оператор break¶
Оператор continue¶
Дополнительная информация¶
Точка следования (англ. Sequence point) — в программировании любая точка программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений уже проявились, а побочные эффекты последующих еще отсутствуют.
Операторы управления определяют, в какой последовательности выполняется программа. Если бы их не было, операторы программы всегда выполнялись бы последовательно, в том порядке, в котором они записаны.
Условные операторы
Условные операторы позволяют выбрать один из вариантов выполнения действий в зависимости от каких-либо условий. Условие – это логическое выражение, т.е. выражение, результатом которого является логическое значение true (истина) или false (ложь).
Оператор if выбирает один из двух вариантов последовательности вычислений.
Конструкция else необязательна. Можно записать:
Если в случае истинности условия необходимо выполнить несколько операторов, их можно заключить в фигурные скобки:
Теперь если x отрицательно, то не только его значение изменится на противоположное, но и будет выведено соответствующее сообщение. Фактически, заключая несколько операторов в фигурные скобки, мы сделали из них один сложный оператор или блок. Прием заключения нескольких операторов в блок работает везде, где нужно поместить несколько операторов вместо одного.
Условный оператор можно расширить для проверки нескольких условий:
Конструкций else if может быть несколько.
Очевидно, что приведенный пример можно переписать с помощью оператора if :
Пожалуй, запись с помощью оператора переключения switch более наглядна. Особенно часто переключатель используется, когда значение выражения имеет тип набора.
Существуют следующие арифметические операторы, поддерживаемые языком C ++:
Реляционные операторы
Существуют следующие реляционные операторы, поддерживаемые языком C ++:
Оператор
Описание
Пример
==
Проверяет, равны ли значения двух операндов или нет, если да, то условие становится истинным.
(A == B) не соответствует действительности.
знак равно
Проверяет, равны ли значения двух операндов или нет, если значения не равны, условие становится истинным.
(A! = B) истинно.
>
Проверяет, превышает ли значение левого операнда значение правого операнда, если да, тогда условие становится истинным.
(A> B) неверно.
=
Проверяет, превышает ли значение левого операнда значение правого операнда, если да, тогда условие становится истинным.
(A> = B) неверно.
>
Двоичный оператор правого сдвига. Значение левых операндов перемещается вправо на количество бит, заданных правым операндом.
A >> 2 даст 15, что составляет 0000 1111
Операторы присваивания
Существуют следующие операторы присваивания, поддерживаемые языком C ++:
Другие операторы
В следующей таблице перечислены некоторые другие операторы, поддерживаемые C ++:
Оператор
Описание
sizeof
Возвращает размер переменной. Например, sizeof (a), где ‘a’ является целым числом и будет возвращать 4.
Если Условие истинно, то оно возвращает значение X, иначе возвращает значение Y.
Используются для ссылки на отдельных членов классов, структур и союзов.
Преобразуют один тип данных в другой. Например, int (2.2000) вернет 2.
Возвращает адрес переменной. Например, & a; даст фактический адрес переменной.
Является указателем на переменную. Например * var; будет указывать на переменную var.
Приоритеты операторов в C ++
Например, x = 7 + 3 * 2; здесь x назначается 13, а не 20, потому что оператор * имеет более высокий приоритет, чем +, поэтому он сначала умножается на 3 * 2, а затем добавляется в 7.
Оператор — это элемент языка, задающий полное описание действия, которое необходимо выполнить. Каждый оператор представляет собой законченную фразу языка программирования и определяет некоторый вполне законченный этап обработки данных. В состав операторов могут входить служебные слова, данные, выражения и другие операторы. В английском языке данное понятие обозначается словом “statement”, означающим также “предложение”.
Каждый оператор в любом языке программирования имеет определенный синтаксис и семантику. Под синтаксисом оператора понимается система правил (грамматика), определяющая его запись с помощью элементов алфавита данного языка, в который наряду с различными символами входят, например, и служебные слова. Под семантикой оператора понимают его смысл, т.е. те действия, которым соответствует запись того или иного оператора. Например, запись i := i + 1 является примером синтаксически корректной записи оператора присваивания в языке Pascal, семантика которого в данном случае такова: извлечь значение ячейки памяти, соответствующей переменной i, сложить его с единицей, результат записать в ту же ячейку памяти.
В большинстве процедурных языков программирования набор операторов практически одинаков и состоит из оператора присваивания, операторов выбора, операторов цикла, оператора вызова процедуры, операторов перехода. Иногда выделяют также пустой (отсутствие действия) и составной операторы. Многие операторы являются способом представления определенных алгоритмических конструкций (см. “Алгоритмические конструкции” ) в языке программирования. Рассмотрим группы операторов подробнее, используя синтаксис языка Pascal.
Оператор присваивания
Присваивание — это действие компьютера, в результате которого переменная получает значение вычисленного выражения (оно помещается в соответствующую переменной ячейку памяти). Для описания такого действия в языках программирования существует оператор присваивания.
В общем виде оператор присваивания записывается так:
Например, в языке Pascal в качестве знака присваивания используется комбинация символов :=. В ряде других языков — знак равенства.
Результатом выполнения оператора присваивания является изменение состояния данных: все переменные, отличные от переменной, стоящей в левой части оператора присваивания, не меняют своего значения, а указанная переменная получает значение выражения, стоящего в правой части оператора присваивания. В большинстве случаев требуется, чтобы тип выражения совпадал с типом переменной. Если это не так, то оператор либо считается синтаксически некорректным, либо производится преобразование типа выражения к типу переменной (см. “Типы данных” ).
Операторы выбора
По-другому эти операторы называют условными операторами. Условные операторы используются для программирования алгоритмов, содержащих алгоритмическую конструкцию ветвление.
В языках программирования имеется несколько видов условных операторов. Полный условный оператор соответствует алгоритмической структуре полного ветвления:
В языке программирования соответствующий условный оператор имеет вид:
if B then S1 else S2
Если выражение B, которое вычисляется в начале выполнения условного оператора, имеет значение “истина”, то будет выполняться оператор S1, в противном случае — оператор S2. Операторы S1 и S2 могут быть составными.
Алгоритмическая структура неполного ветвления реализуется с помощью неполного условного оператора, который имеет вид:
Здесь B — логическое выражение, а S — произвольный оператор. Оператор S будет выполняться, если выражение B окажется истинным.
Если условный оператор реализует всего две ветви выбора (“да” и “нет”), то с помощью оператора варианта (case-оператора) можно запрограммировать многоветвящуюся структуру. Оператор варианта имеет вид:
Выполняется данный оператор так: значение выражения E ищется среди перечисленных в записи оператора значений V1, V2, …, Vn, и если такое значение находится, то выполняется соответствующий оператор S1, S2, …, Sn.
В разных языках программирования синтаксис и даже семантика перечисленных операторов могут отличаться, но возможности, предоставляемые программисту подобными конструкциями, примерно одинаковы.
Пример 1. В статье “Алгоритмические конструкции” 2 был приведен пример записи алгоритма решения обобщенного квадратного уравнения с помощью конструкций ветвления. Приведем фрагмент программы на языке Pascal, реализующий этот же алгоритм:
if c = 0 then writeln(‘x — любое’)
else writeln(‘нет корней’)
В цикле с постусловием тело цикла предшествует условию В. В отличие от цикла с предусловием здесь В — это условие окончания цикла. Оператор цикла с постусловием в Паскале имеет вид:
При такой организации цикла тело цикла S хотя бы один раз обязательно выполнится.
Практически во всех процедурных языках существует оператор цикла c параметром. Схематично его можно представить так:
Здесь значение переменной (параметра цикла) меняется от значения выражения E1 до E2 с шагом E3. Для каждого такого значения параметра цикла выполняется оператор S. В языке Pascal понятие шага в описании этого оператора отсутствует, а сам шаг для целочисленного параметра цикла может быть равен либо 1, либо –1. Оператор “цикл с параметром” используется для программирования циклов с заданным числом повторений. Для программирования итерационных циклов (число повторений которых заранее неизвестно) он не годится.
Оператор вызова процедуры
В статье “Подпрограммы” подробно рассказывается о таком виде подпрограмм, как процедуры. Стандартные подпрограммы языка программирования, которые входят в одну из библиотек подпрограмм, а также пользовательские подпрограммы, описанные внутри данного блока, вызываются с помощью оператора вызова процедуры:
Здесь E1,E2,…,En — переменные или выражения, представляющие собой фактические параметры обращения к процедуре. Наиболее часто используемыми стандартными процедурами являются процедуры ввода и вывода данных (read и write в Pascal).
Вызов процедуры семантически эквивалентен выполнению блока, описанного в качестве тела процедуры, после передачи в него начальных значений некоторых переменных (параметров-значений) или замены имен некоторых переменных (параметров-переменных) на имена фактических переменных, указанных при вызове процедуры.
Пример 3. Пусть у нас описана процедура abc:
procedure abc(a,b:integer;var c: integer);
Вызов этой процедуры abc(2,3,x) эквивалентен блоку действий:
Операторы перехода
Наиболее известным в данной группе операторов является оператор безусловного перехода goto. Если ко всем или некоторым уже имеющимся операторам программы добавить метки, то в программе становится возможным использовать оператор перехода вида:
Метка в данном случае соответствует началу того оператора, с которого должно продолжиться выполнение программы. Такой оператор позволяет записывать в языке программирования алгоритмы, имеющие сколь угодно сложную структуру. Но зачастую использование безусловного перехода неоправданно, т.к. ведет к запутанной, плохо читаемой программе. Практически единственным осмысленным применением оператора goto является выход сразу из нескольких вложенных циклов, например, при обработке двухмерных массивов.
Пример 4. Пусть нам требуется определить, есть ли в двухмерном массиве a элемент, равный 0:
if a[i,j] = 0 then begin
1: if b then write(‘есть’) else write(‘нет’);
Программа, разработанная по правилам структурного программирования, не должна содержать операторов безусловного перехода. Приведенную выше программу без использования оператора goto можно переписать следующим образом:
then writeln(‘Мне ‘,k,’ года’)
else writeln(‘Мне ‘,k,’ лет’)
При рассмотрении операторов цикла полезно предложить одну и ту же задачу запрограммировать тремя разными способами с использованием трех операторов цикла, и наоборот, по условию задачи научиться определять, какой именно оператор цикла является наиболее подходящим в том или ином случае.
Оператор вызова процедуры только на первый взгляд является простым. Здесь важно объяснить правила передачи параметров в процедуры и функции, различие между параметрами-переменными и параметрами-значениями (в последнем случае мы можем передавать не только имя переменной, но и константу или даже выражение соответствующего типа). Формальные и фактические параметры должны соответствовать по типам, но не по именам, что является далеко не очевидным для учеников.
Изучение условного и особенно составного оператора — хороший повод поговорить с учениками о стиле написания программ. Для языка Pascal существует несколько распространенных способов записи структурированных программ, но все они содержат отступы для размещения вложенных структур. Важны для записи программ и комментарии.
Следующий «слой» конструкций языка программирования – операторы. Они создают то, что в обыденном сознании ассоциируется с понятием алгоритм – описание последовательности действий, выполняемых программой, или логика ее работы. Это, в свою очередь, ассоциируется с понятием «блок-схема», что, в целом, довольно близко к истине. На этом уровне языки программирования проявляют завидное единообразие, поскольку количество видов управляющей логики программы ограничено. В Си/Си++ реализован общий для большинства языков программирования «джентльменский набор» управляющих конструкций:
· линейная последовательность действий ;
· условная конструкция (если-то-иначе);
· конструкция повторения (цикл) ;
· переход (и его разновидности).
Сразу же отметим, что этот набор является функционально избыточным, и для записи любого алгоритма достаточно только три вида.
· элементы блок-схемы соответствуют основным компонентам системы команд компьютера с размещением программы в линейной памяти (см. 1.2) – командам обработки данных, проверки условий и безусловных (условных) переходов;
· блок-схемы являются естественным инструментом технологии «исторического» программирования, базирующейся на развертке процесса выполнения проектируемой программы во времени (см. 3.2).
Блок-схема содержит элементы трех видов:
Принцип вложенности и структурированные блок-схемы
Рис.15.1. Структурированные конструкции и блок-схема
Операторы линейной последовательности действий
for (i=0; i // Обычный цикл
for (i=0; A[i]!=0 && i // Цикл с пустым оператором
Рис.15.2. Линейная последовательность операторов
В соответствии с принципом вложенности элементами последовательности могут быть не только элементарные действия (простые операторы), но и вложенные синтаксические конструкции, которые на текущем уровне также выполняются последовательно друг за другом.
В Си используется другой принцип: если составной частью управляющей конструкции является единственный оператор, то он никак синтаксически не выделяется. Если же составной частью является последовательность операторов, то она заключается в фигурные скобки ( <>) и образует блок.
Условные операторы
Единственный условный оператор имеет две разновидности: с else и без него.
Операторы цикла
for (i=0; i тело цикла для i…
Операторы перехода
· оператор генерации исключения throw используется в Си++ как средство обработки ошибок, выполняя, в том числе, и действия, эквивалентные оператору return (см. 12.2)
for (i=0; i m1: i++) // иллюстрация выполнения continue, break, return
if (A[i]==0) continue; // goto m1;
if (A[i]==-1) return; // goto m2;
if (A[i] break; // goto m3;
Оператор switch
case 1: n=n+2; break; // m1: n=n+2; goto mend;
case 2: n=0; break; // m2: n=0; goto mend;
case 4: n++; break; // m4: n++; goto mend;
default: n=-1; // mdef: n=-1;
sign=0; // Ветвь для значения c, равного ‘+’,
case ‘-‘: sign=1; // для значения ‘-‘
Еще один «джентльменский набор»
Набор управляющих конструкций алгоритма может быть различным и избыточным. Но минимально необходимой является триада: для архитектурно-ориентированной логики – это действие, условие, переход. Структурированные конструкции – последовательность, выбор (ветвление), повторение (цикл) – также обладают необходимой полнотой. Но на практике встречается еще одна, довольно экзотическая триада: последовательность, ветвление и рекурсия. Являясь технологическим приемом программирования (см. 7.1), она способна, как минимум, заменить конструкцию повторения (цикл). Существуют языки программирования (ПРОЛОГ), а также математические формализмы (формальные грамматики, частично-рекурсивные функции), опирающиеся на эту триаду.
Технология структурного программирования
Функциональное программирование (ПРОЛОГ), представление синтаксиса в формальных грамматиках (трансляторы), частично-рекурсивные функции (теория алгоритмов)
Подводные камни
Особенность синтаксиса Си состоит в том, что последовательности управляющих конструкций, отличающиеся буквально на один символ, могут быть синтаксически правильными, но давать различные последовательности выполнения действий в программе. Это касается, прежде всего, тела цикла, где обнаруживается максимальное количество ошибок программирования (но то же самое можно отнести и к составным частям условного оператора). Итак, тело цикла может иметь четыре различных варианта реализации :
· простой (первичный) оператор – выражение, ограниченное символом «;»;
· единственный оператор, имеющий произвольную внутреннюю структуру своего тела – условный, цикл, переключатель;
Отсюда следует, что символ «точка с запятой» нельзя расставлять «для надежности», он может выступить в качестве пустого оператора, отрезав заголовок цикла от его настоящего тела. Аналогично, при усложнении тела цикла в процессе модификации программы (вместо одного оператора – последовательность из нескольких) не нужно забывать объединять получающиеся последовательности в блоки. Иначе к телу цикла будет отнесен только первый из них.
for (int s=0,i=0; i // Сорок раз по разу
for ( int s =0, i =0; i i ++) ; // Ни разу сорок раз
s = s + A [ i ]; // Один раз – после (со значением A [40] вне массива)