Лекция 3. Операторы и выражения¶
Архитектура программы¶
- символы
- лексемы
- операторы и выражения
- функции
- модули

Выражения и операторы¶
Определение
Операторы и выражения - запись действия в программе.
- Программа строится из операторов.
- Операторы включают в себя выражения.
- Выражение представляет собой комбинацию операций и операндов.
- Выражение имеет некоторое значение (которое можно сохранить в памяти), а оператор значения не имеет.
- У оператора есть некоторый результат (побочный эффект), например повторение действия.
Понятие оператора¶
Существуют операторы преобразования данных и операторы управления работой программы.
Операции¶
Характеристики операций¶
- Ассоциативность (левоассоциативные, правоассоциативные)
- Количество аргументов (унарные, бинарные, тернарные)
- Приоритет
- Префиксная и постфиксная форма (для ++, — —)
Ассоциативность
a=b=c=d=e
a+b+c+d+e
Аргументы
a+b
!a
(a>b)?c:d
Приоритет
a+b*c/d-e
a=b==c
Приоритет операций¶
Результат операций¶
В языке Си у операций могут быть следующие результаты:
- Числовая константа, означающая результат присвоения, математического выражения, адрес ячейки памяти.
- Числовая константа, означающая логическое значение (0 - ложь, 1 - истина).
- Ссылка на область памяти.
В Си не имеется встроенного логического типа со значениями true и false, но любое ненулевое целое значение означает истину, а нулевое - ложь.
Арифметические операции¶
Знак | Операция |
|
Умножение |
/ | Деление и целочисленное деление нацело |
% | Деление по модулю и остаток от деления |
|
Сложение |
|
Вычитание |
Знак / всегда означает деление. Однако если с обеих сторон от этого знака стоят целые величины (константы, переменные или их комбинации), он означает целочисленное деление. Если в результате такого деления получается остаток, С++ его отбрасывает.
Знак % означает выделение остатка при целочисленном делении. Эта операция требует, чтобы с обеих сторон от ее знака стояли целые величины
a = 10/2; // 5 (остатка нет)
b = 300/100; // 3 (остатка нет)
c = 10/3; // 3 (остаток отброшен)
d = 300/165; // 1 (остаток отброшен)
e = 10%3; // 1 (остаток)
f = 20/4; // 5 (остатка нет)
Присваивание¶
Особенностью Си является возможность комбинирования операции присваивания с другими операциями, а также выполнение множественного присваивания.
a=b=c=d=10;
value=15*(a=9-b);
Существуют два класса выражений:
l-value (левостороннее выражение. Может стоять слева от присваивания)
r-value (правостороннее выражение. Может стоять справа от присваивания)
Составное присваивание¶
Эта группа операций позволяет совместить арифметику и присваивание, что дает выразительность и удобочитаемость программам.
Операция | Пример | Эквивалент |
|
B+ = 500; | B= b+ 500; |
– = | С– = 50; | C = с – 50; |
|
D* = 1.2; | D = d*1.2; |
/ = | F/ = 50; | f = f/.50; |
% = | m% = 7; | M = m% 7; |
Преобразование типов¶
В различных выражениях могут встречаться данные как одного, так и разных типов. Компилятор может выполнять операцию приведения типов по-умолчанию, действуя согласно простого правила: короткие типы приводятся к длинным. Если необходимо изменить стандартное преобразование, то вводят явное приведение типов.
int a=10,b=20;
float c=7.9,d;
d=c+(float)a; // a приводится к float
b=(int)(d+(float)b); // двойное приведение
Сравнение¶
Операции сравнения являются бинарными и используются для сравнения двух значений
Операции | Описание |
== | Равно |
> | Больше |
< | Меньше |
>= | Больше или равно |
<= | Меньше или равно |
!= | Не равно |
Результатом операции сравнения выступают целые числа 0 и 1. Первое означает ложь, второе - истину.
Не следует путать присваивание = и равенство ==
Логические операции¶
Для создания более сложных выражений из простых существуют логические операции, которые реализуют логическое отрицание, умножение и сложение.
Для логических умножения и сложения действует принцип: если результат первого подвыражения определяет результат всего выражения, то второе и последующие подвыражения не выполняются.
d = (a==b) && printf("Hello\n");
В приведенном примере, если \(a\) равно \(b\) , строка Hello появляется на экране, иначе не появляется.
Значения операций¶
Пример выражения | Результат |
-4+6 | 2 |
c=3+8 | 11 |
5>3 | 1 |
6+(c=3+8) | 17 |
6+c=3+8 | Ошибка! |
z=(x>y)?1:0;
max=(a>b)?a:b;
Инкремент и декремент¶
Данные операции могум использоваться в префиксной и постфиксной формах. основная разница - изменение приоритета.
Код
int a=0,b=0;
a=++b;
будет эквивалентен следующему
int a=0,b=0;
b=b+1;
a=b;
Код
int a=0,b=0;
a=b++;
будет эквивалентен следующему
int a=0,b=0;
a=b;
b=b+1;
Правила вычислений
- Постфиксная форма
- СНАЧАЛА переменная изменяется на 1;
- и только после этого используется в выражении.
- Префиксная форма
- старое значение переменной сохраняется для использования в дальнейшем выражении, в котором встретилась эта переменная;
- и только ПОСЛЕ этого ее значение СРАЗУ ЖЕ изменяется на 1.
Операции инкремента/декремента¶
Корректные выражения
int x=10;
int y=2;
int z=x++ + --y;
// вывод 11
printf("%d",z);
Некорректные выражения
int y=2,n=3;
// результат может быть разным
int nextnum=(y+n++)*n;
// вывод 4 15 в MS VC++ 2005
printf("%d %d",n,nextnum);
z=++x + x++; // результат не определён
++x++; // попытка изменить l-value
Что можно сказать о следующей программе?
int main()
{
int x=10,y=20;
x++=--y;
printf("%d %d\n",x,y);
return 0;
}
pause
Ошибка компиляции: l-value required as left operand of assignment
Неопределенное поведение¶
Определение (см. Википедию)
Неопределённое поведение (англ. undefined behaviour) — свойство некоторых языков программирования (наиболее заметно в Си), программных библиотек и аппаратного обеспечения в определённых маргинальных ситуациях выдавать результат, зависящий от реализации компилятора (библиотеки, микросхемы) и случайных параметров
Пример:
int i = 5;
i = ++i + ++i;
При его выполнении переменная i может принять значения 13 или 14 для C/C++, 13 для Java, PHP и C#, 12 при реализации на LISP.
Неопределенность в языке C/C++ связана с тем, что согласно стандартам С и С++ побочные эффекты (то есть инкремент в данном случае) могут быть применены в любой удобный для компилятора момент между двумя точками следования (см. раздел Дополнительная информация).
Операторы¶
Основные алгоритмические конструкции¶
Основная теорема структурного программирования
Программа для решения любой задачи может быть составлена из комбинации следования, ветвления и цикла (Бойм-Якопини, 1966).
- следование
- ветвление
- цикл



Оператор if¶
Простые формы
if(exp1)
exp2;
if(exp1)
exp2;
else
exp3;
if(exp1)
exp2;
else if(exp3)
exp4;
Составные формы
if(exp1)
{
...
...
}
else if (expr2)
{
...
...
}
else
{
...
...
}
Примеры if¶
Несколько версий одной и той же программы
int main()
{
int val;
printf("Enter a number: ");
scanf("%d",&val);
if(val>0)
puts("You entered a positive number!");
else if(val<0)
puts("You entered a negative number!");
else
puts("You entered ZERO!");
return 0;
}
int main()
{
char mes[]="You entered";
char numbers[][20]={"a positive number","a negative number","ZERO"};
int val;
printf("Enter a number: ");
scanf("%d",&val);
if(val>0)
printf("%s %s!\n",mes,numbers[0]);
else if(val<0)
printf("%s %s!\n",mes,numbers[1]);
else
printf("%s %s!\n",mes,numbers[2]);
return 0;
}
int main()
{
char mes[]="You entered";
char numbers[][20]={"a positive number", "a negative number","ZERO"};
int val,choice;
printf("Enter a number: ");
scanf("%d",&val);
if(val>0)
choice=0;
else if(val<0)
choice=1;
else
choice=2;
printf("%s %s!\n",mes,numbers[choice]);
return 0;
}
int main()
{
char mes[]="You entered";
char numbers[][20]={"a positive number", "a negative number","ZERO"};
int val;
printf("Enter a number: ");
scanf("%d",&val);
printf("%s %s!\n",mes,numbers[val>0?0:(val<0?1:2)]);
return 0;
}
Удачные/неудачные конструкции¶
if(coord_x>20)
if(coord_x<30)
flag=0;
else
flag=1;
if(coord_x>20)
{
if(coord_x<30)
flag=0;
else
flag=1;
}
if(coord_x>20)
if(coord_x<30)
if(coord_y<40)
;
else
flag=1;
if(coord_x>20 && coord_x<30)
flag=0;
else
flag=1;
Удачные и неудачные конструкции¶
Среди всех приведенных конструкций наиболее удачной следует признать последнюю, так как она лишена нагромождений и воспринимается однозначно.
Если необходимо сравнить значение переменной с набором констант, то лучше отказаться от if в пользу switch
Сложные выражения¶
Замечание
С позволяет конструировать очень сложные выражения. Эта сложность должна быть оправдана.
int coord_x;
int ret,flag;
if((ret=scanf("%d",&coord_x))!=1||(coord_x<0||coord_x>40))
printf("Error!\n");
else
printf("flag=%d\n",(coord_x%10>0)?flag=1:flag=0);
Оператор switch¶
Оператор switch¶
switch(exp1)
{
case const1:
exp2;
case const2:
exp3;
...
default:
expN;
}
switch(exp1)
{
case const1:
exp2;
break;
case const2:
exp3;
break;
default:
expN;
}
int count=0;
int count_a=0;
int count_b=0;
...
switch(ch)
{
case 'a':
count_a++;
case 'b':
count_b++;
default:
count++;
}
switch(ch)
{
case 'a':
count_a++;
break;
case 'b':
count_b++;
break;
default:
count++;
}
Вопрос: Чему будут равны значения переменных, если строка: babah!?
Оператор while¶
Операторы цикла¶
В Си существуют операторы с предусловием и постусловием. Особенность первых состоит в том, что условное выражение находится у них перед телом, так что возможно выполнить тело один раз, а возможно - ни разу. Циклы с постусловием обеспечивают выполнение тела хотя бы один раз, проверочное выражение находится у них после описания тела.
- Циклы с предусловием: while(), for()
- Циклы с постусловием: do..while()
Оператор while¶
while (exp1)
exp2;
while(exp1)
{
expr2;
expr3;
...
}
int count=10;
while(count>=0)
{
puts("Hello!");
count--;
}
char str[]="A simple string";
int i=0;
while(str[i]!='\0')
putchar(str[i++]);
char str[]="A simple string";
int i=0;
while(str[i]!=0)
putchar(str[i++]);
char str[]="A simple string";
int i=0;
while(str[i])
putchar(str[i++]);
char str[]="A simple string";
int i=0,count=0;
while(str[i])
{
if(str[i]>='A'&&str[i]<='Z')
count++;
i++;
}
Найдите ошибку:
char str[]="A simple string";
int i=0,count=0;
while(str[i])
if(str[i]>='A'&& str[i++]<='Z')
count++;
char str[]="A simple string";
int i=0;
while(str[i])
if(str[i]>='A'&&str[i]<='Z')
putchar(str[i]+'a'-'A');
char str[]="A simple string";
int len=0;
while(str[len++]);
printf("String length:%d\n",
len-1);
Ошибки при организации while¶
В чем состоит ошибка?
int main(){ /* печать фразы 10 раз */
{
int i;
while(i < 10){
printf("%d\n", i+1);
i++;
}
}
Оператор do while¶
Оператор do while¶
Это цикл с постусловием. Тело цикла выполняется как минимум 1 раз
do
exp1;
while(exp2);
do
{
exp1;
exp2;
...
}
while(exp1);
int val;
do
{
fflush(stdin);
printf("Enter a positive number\n");
scanf("%d",&val);
}
while(val<=0);
Оператор for¶
Оператор for¶
Формат
for(exp1;exp2;exp3)
exp4;
for(exp1;exp2;exp3)
{
exprs;
}
Порядок выполнения
exp1
exp2 ->
exp4
exp3
exp2 ->
int main()
{
int top=0, bot=0, len;
char str[256],temp;
fgets(str,256,stdin);
len=strlen(str); // определяем длину введенной строки
if(str[len-1]=='\n') // удаляем хвостовой \n
str[len-1]=0;
while(str[bot++]); // bot указывает на символ после \0
for ( bot-=2; top < bot ; top++, bot--)
{
temp=str[top];
str[top]=str[bot];
str[bot]=temp;
}
puts(str);
return 0;
}
int main()
{
const int ROWS=6;
const int CHARS=6;
int row;
char ch;
for(row=0;row<ROWS;row++)
{
for(ch=('A'+row);ch<('A'+CHARS);ch++)
putchar(ch);
putchar('\n');
}
return 0;
}
Оператор for¶
ABCDEF
BCDEF
CDEF
DEF
EF
F
Операторы управления¶
- goto - безусловный переход на метку.
- return - возвращение из функции.
- break - прерывание тела цикла и switch
- continue - переход на следующую итерацию цикла.
Оператор goto усложняет отладку программы и сильно портит стиль разработчика (‘’спагетти-код’‘)!
Спагетти-код¶
Пример спагетти кода на языке BASIC:
10 i = 0
20 i = i + 1
30 IF i <= 10 THEN GOTO 70
40 IF i > 10 THEN GOTO 50
50 PRINT "Программа завершена."
60 END
70 PRINT i; " квадрат = "; i * i
80 GOTO 20
Тоже самое, но без goto:
FOR i = 1 TO 10
PRINT i; " квадрат = "; i * i
NEXT i
PRINT "Программа завершена."
Оператор break¶
Назначение break - прерывать цикл или тело switch:
/* Распечатываем числа с 1 по 15 и выходим */
int i=1;
for(;;)
{
printf("%d\n",i);
if(i==15)
break;
}
Оператор continue¶
/* Распечатываем нечётные числа с 1 по 100 */
for(int i=1;i<=100;i++)
{
if(i%2==0)
continue;
printf("%d\n",i);
}
Дополнительная информация¶
Определение
Точка следования (англ. Sequence point) — в программировании любая точка программы, в которой гарантируется, что все побочные эффекты предыдущих вычислений уже проявились, а побочные эффекты последующих еще отсутствуют.
В C и C++ определены следующие точки следования:
- Между вычислением левого и правого операндов в операторах && (логическом И), || (логическом ИЛИ) и операторах-запятых. Например, в выражении *p++ != 0 && *q++ != 0 все побочные эффекты левого операнда texbf{*p++ != 0} проявятся до начала каких либо действий в правом.
- Между вычислением первого и второго или третьего операндов в операторе условия. В строке a = (*p++) ? (*p++) : 0 точка находится после первого операнда *p++, при выполнении второго p уже увеличена на 1.
- В конце всего выражения. Эта категория включает в себя инструкции-выражения a=b;, выражения в инструкциях return, управляющие выражения в круглых скобках инструкций ветвления if или switch и циклов while или do-while и все три выражения в круглых скобках цикла for.
- Перед входом в вызываемую функцию. Порядок, в котором вычисляются аргументы не определен, но эта точка следования гарантирует, что все ее побочные эффекты проявятся на момент входа в функцию. В выражении f(i++) + g(j++) + h(k++) каждая из трёх переменных: i, j и k, принимает новое значение перед входом в f, g и h соответственно. Однако, порядок вызова функций f(), g(), h() неопределён, следовательно неопределён и порядок инкремента i, j, k. Значения j и k в теле функции f оказываются неопределенными. Следует заметить, вызов функции нескольких аргументов f(a,b,c) не является случаем применения оператора-запятой и не определяет порядок вычисления значений аргументов.
- При возврате из функции, на момент когда возвращаемое значение будет скопировано в вызывающий контекст.(Явно описана только в стандарте С++, в отличие от С.)
- В объявлении с инициализацией на момент завершения вычисления инициализирующего значения, например, на момент завершения вычисления (1+i++) в int a = (1+i++);
Вопросы для самоконтроля¶
- Какие уровни образуют архитектуру программы на С?
- Что такое оператор? Выражение?
- Чем оператор отличается от выражения?
- Какие бывают операторы?
- Приведите примеры операторов и выражений.
- Приведите пример классификации операций.
- Перечислите основные характеристики операций.
- Чем левоассоциативные операции отличаются от правоассоциативных?
- Как называются операции в зависимости от количества аргументов?
- Какие операции относятся к высоко приоритетным? К низко приоритетным?
- Что представляет собой истинное значение в Си? Ложное значение?
- Чем отличается l-value-выражение от r-value?
- В чём заключаются особенности присваивания в языке Си?
- Как осуществляется преобразование типов?
- Что такое явное и неявное преобразование типов?
- Как работает механизм логических операций?
- Какие бывают операции инкремента/декремента?
- Как можно выразить операцию инкремента в разных формах?
- Как вычисляется выражение с инкрементом в префиксной форме?
- Как вычисляется выражение с инкрементом в постфиксной форме?
- Какими особенностями обладают операции инкремента/декремента?
- Что означает термин ‘’неопределённое поведение’‘?
- Какие алгоритмические конструкции мы относим к основным при структурном программировании?
- Как можно классифицировать операторы?
- Опишите варианты оператора if. Для чего используется этот оператор?
- На что влияет неудачно выбранная конструкция оператора?
- Какой должна быть сложность конструкций языка С?
- Опишите формат оператора switch.
- В чем назначение оператора switch?
- Опишите формат оператора while. Для чего используется этот оператор?
- Как можно использовать while при обработке строк?
- Какие ошибки могут возникать при использовании while?
- Опишите формат оператора do..while.
- Чем цикл do while отличается от цикла while?
- Опишите формат оператора for.
- Чем оператор for отличается от других операторов цикла?
- Как с помощью for инвертировать символы в строке?
- Перечислите операторы управления ходом выполнения программы.
- Что такое спагетти-код?
- Что такое точки следования?