Similar presentations:
Выражения языка С
1. Выражения языка С
лекция 42. План лекции
• Классы, приоритеты и ассоциативностьопераций языка Си
• Выражения l-value
• Порядок вычисления выражений, точки
следования, побочные эффекты
• Выражения и подвыражения
• Неявные преобразования типов
3. Обзор операций языка Си 1/3
• Операции Си делятся на 5 классов по числу ирасположению аргументов
• Запись одной операции состоит из одной или
нескольких лексем
Класс
Число аргументов
Положение операции отн.
аргументов
Атомарные
0
Префиксные
1
Перед
Постфиксные
1
После
Бинарные
2
Между
Тернарные
3
Между
4. Обзор операций языка Си 2/3
• Операции связываются с аргументами повозрастанию своих приоритетов
• Приоритеты задаются целыми числами
П(*)
13
12
12
П(+)
12
13
12
Возможная расстановка скобок в х*х+у*у
(х*х)+(у*у)
(х*(х+у))*у, х*((х+у)*у)
((х*х)+у)*у, (х*(х+у))*у, (х*х)+(у*у),
х*(х+(у*у)), х*((х+у)*у)
5. Обзор операций языка Си 3/3
• Ассоциативность бинарных операций задаетрасстановку скобок в выражениях, содержащих
операции одного приоритета
• Левоассоциативные -- слева направо
• Правоассоциативные -- справа налево
• Операции языка Си одного приоритета имеют
одинаковую ассоциативность
– Иначе расстановка скобок неоднозначна
А(-) А(+) Расстановка скобок в х-х+у-у
л
л
((х-х)+у)-у
п
п
х-(х+(у-у))
6. Приоритеты операций в языке Си
ЛексемыОперация
Класс
Приор-т
Ассоц-ность
Переменные
Константы
Доступ к значению константы или переменной
атомарный
16
нет
a[k]
Доступ к элементу массива
постфиксный
16
слева направо
f(…)
Вызов функции
постфиксный
16
слева направо
.
Доступ к полю структуры или объединения
постфиксный
16
слева направо
->
Доступ к полю структуры или объединения
через указатель
постфиксный
16
слева направо
k++ k--
Доступ к значению k и послед. увеличение или
уменьшение k на 1
постфиксный
16
слева направо
++k --k
Увеличение или уменьшение k на 1 и послед.
доступ к полученному значению k
префиксный
15
справа налево
sizeof
Размер значения или типа в байтах
префиксный
15
справа налево
~
Побитовое НЕ
префиксный
15
справа налево
!
Логическое НЕ
префиксный
15
справа налево
-+
Смена знака числа (-) или НОП (+)
префиксный
15
справа налево
&
Взятие адреса
префиксный
15
справа налево
7. Приоритеты операций в языке Си
ЛексемыОперация
Класс
Приор-т
Ассоциативность
*
Доступ через указатель
префиксный
15
справа налево
(имя типа)
Явное изменение (преобразование) типа
префиксный
14
справа налево
*/%
Умножение, деление, остаток от деления
бинарный
13
слева направо
+-
Сложение, вычитание чисел
бинарный
12
слева направо
<< >>
Сдвиг влево или вправо в 2 с.с.
бинарный
11
слева направо
< > <= >=
Сравнение чисел
бинарный
10
слева направо
== !=
Проверка равенства и различия
бинарный
9
слева направо
&
Побитовое И
бинарный
8
слева направо
^
Побитовое исключающее ИЛИ
бинарный
7
слева направо
|
Побитовое ИЛИ
бинарный
6
слева направо
&&
Логическое И
бинарный
5
слева направо
||
Логическое ИЛИ
бинарный
4
слева направо
с ? в1 : в2
в1 (если с != 0) или в2 (если с == 0)
тернарный
3
справа налево
8. Приоритеты операций в языке Си
ЛексемыОперация
Класс
Приор-т
Ассоц-ность
= += -= *= /= %= <<= >>= &= ^= |=
Вычисление правого аргумента
и послед. запись полученного
значения в ячеку памяти,
определяемую левым
аргументом (присваивание)
бинарный
2
справа
налево
,
Последовательное вычисление
аргументов
бинарный
1
слева
направо
9. Первичные выражения
Идентификатор переменной или функции
Константа
Строковый литерал (является lvalue)
Выражение, взятое в круглые скобки
10. Доступ к элементу массива
• A[k]• A
11. Выражения и подвыражения
<выражение>::=<выражение-присваивания>
|
<выражение> ',' <выражение-присваивания>
<выражение-присваивания> ::=
<условное-выражение>
|
<унарное-выражение> <оператор-присваивания>
<выражение-присваивания>
<оператор-присваивания>::= '=' | '*=' | '/=' | '%=' | '+=' | '-=' |
'<<=' | '>>=' | '&=' | '^=' | '|='
12. Выражения и подвыражения
<условное-выражение> ::=<логическое-ИЛИ-выражение>
|
<логическое-ИЛИ-выражение> '?' <выражение> ':'
<условное-выражение>
<константное-выражение> ::= <условное-выражение>
<логическое-ИЛИ-выражение>::=
<логическое-И-выражение>
|
<логическое-ИЛИ-выражение> '||'
<логическое-И-выражение>
13. Выражения и подвыражения
<логическое-И-выражение>::=<ИЛИ-выражение>
|
<логическое-И-выражение> '&&'
<ИЛИ-выражение>
<ИЛИ-выражение>::=
<исключающее-ИЛИ-выражение>
|
<ИЛИ-выражение> '|'
<исключающее-ИЛИ-выражение>
<исключающее-ИЛИ-выражение>::=
<И-выражение>
|
<исключающее-ИЛИ-выражение> '^'
<И-выражение>
14. Выражения и подвыражения
<И-выражение>::=<выражение-равенства>
|
<И-выражение> '&' <выражение-равенства>
<выражение-равенства>::=
<выражение-отношения>
|
<выражение-равенства> '=='
<выражение-отношения>
|
<выражение-равенства> '!='
<выражение-отношения>
15. Выражения и подвыражения
<выражение-отношения>::=<сдвиговое-выражение>
|
<выражение-отношения> '<'
<сдвиговое-выражение>
|
<выражение-отношения> '>'
<сдвиговое-выражение>
|
<выражение-отношения> '<='
<сдвиговое-выражение>
|
<выражение-отношения> '>='
<сдвиговое-выражение>
x >> 2
x < y < z << 2
(x < y) < (z << 2)
16. Выражения и подвыражения
<сдвиговое-выражение>::=x+y
<аддитивное-выражение>
x >> y+z
|
<сдвиговое-выражение> '>>'
<аддитивное-выражение>
x << (y+z)
|
<сдвиговое-выражение> '<<'
<аддитивное-выражение>
<аддитивное-выражение>::=
x*y
<мультипликативное-выражение>
x+y+z
|
<аддитивное-выражение> '+'
<мультипликативное-выражение>
|
<аддитивное-выражение> '-'
<мультипликативное-выражение> x - y - z
17. Выражения и подвыражения
<мультипликативное-выражение>::=(double)x
<выражение-приведенное-к-типу>
x*y
|
<мультипликативное-выражение> '*'
<выражение-приведенное-к-типу>
(x / y) / z
|
<мультипликативное-выражение> '/'
<выражение-приведенное-к-типу>
x%y
|
<мулътипликативное-выражение> '%'
<выражение-приведенное-к-типу>
18. Выражения и подвыражения
<выражение-приведенное-к-типу>::=<унарное-выражение>
&x
|
'(' <имя-типа> ')'
(int*) &x
<выражение-приведенное-к-типу>
<унарное-выражение>::=
x++
<постфиксное-выражение>
++(x--)
|
'++' <унарное-выражение>
--(x++)
|
'--' <унарное-выражение>
* (int*) &x
|
<унарный-оператор>
<выражение-приведенное-к-типу>
sizeof(x)
|
'sizeof' <унарное-выражение>
|
'sizeof' '(' <имя-типа> ')'
sizeof(int)
<унарный-оператор>::= '&' | '*' | '+' | '-' | '~' | '!'
19. Выражения и подвыражения
<постфиксное-выражение>::=<первичное-выражение>
|
<постфиксное-выражение> '[' <выражение> ']'
|
<постфиксное-выражение>
'(' [<список-аргументов-выражений>] ')'
|
<постфиксное-выражение> '.' <идентификатор>
|
<постфиксное-выражение> '->' <идентификатор>
|
<постфиксное-выражение> '++'
|
<постфиксное-выражение> '--'
x
A[x]
f (x, y)
point.x
point->x
i++
i--
20. Выражения и подвыражения
<первичное-выражение>::=<идентификатор>
|
<константа>
|
<строка>
|
'(' <выражение> ')'
x
1.25f
"ABCDEF"
(x+y)
21. Выражения и подвыражения
<константа>::=<целая-константа>
|
<символьная-константа>
|
<константа-с-плавающей-точкой>
|
<константа-перечисление>
22. Выражения и подвыражения
<список-аргументов-выражений>::=<выражение-присваивания>
|
<список-аргументов-выражений> ','
<выражение-присваивания>
23.
• Выражения и подвыражения• Классы, приоритеты и ассоциативность
операций языка Си
• Далее
– Выражения l-value
– Порядок вычисления выражений, точки
следования, побочные эффекты
– Неявные преобразования типов
24. Выражения l-value
• Ошибка или нет:– (A[i] < A[j] ? A[i] : A[j]) = 1 // min(A[i], A[j]) заменить на 1
– A[ A[i] < A[j] ? i : j ] = 1
• Выражения языка Си, значениям которых гарантированно
соответствует ячейка памяти, называются l-value
• Значениям, которые хранятся только в регистрах процессора, не
соответствует никакая ячейка памяти
• Только 5 видов выражений в языке Си являются l-value – см.
след. слайд
– Остальные виды выражений – не l-value
25. Выражения l-value
• l-value получаются при следующих операциях–
–
–
–
Доступ к значению переменной
Доступ через указатель *
Доступ к элементу массива a[k]
Доступ к полю структуры или объединения
student.name
– Доступ к полю структуры или объединения через
указатель pstudent->name
• Все остальные операции дают выражения, не
являющиеся l-value
26. Выражения l-value
• Операции, требующие l-value– Левый аргумент во всех видах присваивания =, += и т.п.
– Взятие адреса &
– Префиксные и постфиксные ++ и --
27. Выражения l-value
• Пример 1int x; x = 2; // x – l-value
int A[10]; A[5] = 5+x; // A[5] – l-value, 5+x – не l-value
• Пример 2
int x, y;
(x < y ? x : y) = 1;
*(x < y ? &x : &y) = 1;
// ошибка, т.к. (x < y ? x : y) не l-value
// ОК, т.к. *(x < y ? &x : &y) – l-value
• Пример 3
(A[i] < A[j] ? A[i] : A[j]) = 1;
A[ A[i] < A[j] ? i : j ] = 1;
// ошибка
// ОК
28. Точки следования, побочные эффекты
• Побочный эффект вычисления выражения– это факт изменения содержимого ячеек
памяти в процессе вычисления выражения
– Присваивание
• x = 1;
– Сложный побочный эффект
• i = 0; A[i++] = i++; // чему равно i – 0 или 1?
• В каком порядке выполнятся = и ++?
29. Точки следования, побочные эффекты
• Точка следования (sequence point) -- точкапрограммы, в которой гарантируется, что
все побочные эффекты предыдущих
вычислений уже проявились, а побочные
эффекты последующих ещё отсутствуют
30. Точки следования, побочные эффекты
1. Между вычислением левого и правого аргументов воперациях &&, || и , (запятая)
2. Между вычислением первого и второго или
третьего аргументов в операции ?:
3. В конце всего выражения
4. Перед входом в вызываемую функцию
5. В объявлении с инициализацией на момент
завершения вычисления инициализирующего
значения
6. В остальном порядок выполнения операций
определяет компилятор
31. Точки следования, побочные эффекты
• Пример 1while (*p++ != 0 && *q++ != 0) *p = *q;
• Все побочные эффекты *p++ != 0 проявятся
до начала вычисления *q++ != 0
– Правило 1
• Все побочные эффекты *p++ != 0 и *q++ != 0
проявятся до начала вычисления *p = *q
– Правило 3
• Что делает этот цикл while?
32. Точки следования, побочные эффекты
• Пример 2int A[3] = {1,0,2}, *p = A;
int a = (*p++) ? (*p++) : 0; // чему равно a?
• Точка следования находится после первого *p++
• p уже увеличена на 1 при вычислении второго
*p++
33. Точки следования, побочные эффекты
• Пример 3int i = 0, j = i++, k = i++;
int x = f(i++) + g(j++) + h(k++);
• Каждая из переменных i, j и k принимает новое
значение перед входом в f, g и h соответственно
• Порядок вызова функций f(), g(), h() неопределён
• Порядок инкремента i, j, k неопределён
• Если i, j и k – глобальные переменные, то значения j
и k неопределены внутри f, значения i и k
неопределены внутри g, значения i и j
неопределены внутри h
34. Неявные преобразования типов
• Над числами• Над указателями
• Других нет
35. Неявные преобразования чисел
Если какой-либо из аргументов имеет тип long double, то другой приводится к long double
В противном случае, если какой-либо из аргументов имеет тип double, то другой
приводится к double
В противном случае, если какой-либо из аргументов имеет тип float, то другой приводится к
float
В противном случае для обоих аргументов осуществляется целочисленное повышение;
затем, если один из аргументов имеет тип unsigned long int, той другой преобразуется в
unsigned long int
В противном случае, если один из аргументов принадлежит типу long int, а другой -unsigned int, то результат зависит от того, покрывает ли long int все значения unsigned int
–
–
Если это так, то unsigned int приводится к long int
Если нет, то оба аргумента преобразуются в unsigned long int
В противном случае, если один из аргументов имеет тип long int, то другой приводится к
long int
В противном случае, если один из аргументов -- unsigned int, то другой приводится к
unsigned int
В противном случае оба аргумента имеют тип int
Подробно о каждом преобразовании см. след. слайды
36. Неявные преобразования чисел
• Целочисленное повышение• Значения типов enum, signed char, short int,
unsigned char, unsigned short int
автоматически преобразуются в int (если
значение представимо как int) или в
unsigned int (в противном случае)
• Для всех известных компиляторов enum,
char, short int представимы как int
37. Неявные преобразования чисел
• Целочисленные преобразования из типа со знакомST в тип без знака UT
– Если sizeof(ST) == sizeof(UT), то битовое представление
не меняется
– Если sizeof(ST) < sizeof(UT), то битовое представление со
знаком дополняется старшими нулями
– Если sizeof(ST) > sizeof(UT), то старшие разряды
отбрасываются
• Целочисленные преобразования из типа без знака
UT в тип со знаком ST
– Если sizeof(ST) >= sizeof(UT), то битовое представление
не меняется
– Если sizeof(ST) < sizeof(UT), то результат зависит от
компилятора
38. Неявные преобразования чисел
• Преобразования целые <--> числа сплавающей точкой
– С плавающей точкой --> целое
• Дробная часть отбрасывается
– int x = (int)1.25; // x == 1
• Если полученное значение выходит из диапазона целого
типа, то результат неопределен
– int x = (int)1e20; // рез-т неопределён
– Целое --> с плавающей точкой
• Если целое входит в диапазон типа с плавающей, но
представляется неточно, то одно из двух ближайших
значений с плавающей
• Если выходит из диапазона типа с плавающей, то
результат неопределен
39. Неявные преобразования чисел
• Преобразования чисел с плавающей точкойиз типа F1 в F2
– Если sizeof(F1) <= sizeof(F2), то значение не
меняется (но может измениться битовое
представление)
– Если sizeof(F1) > sizeof(F2) и входит в диапазон
F2, то одно из двух ближайших значений
– Если sizeof(F1) > sizeof(F2) и не входит в
диапазон F2, то неопределено
40. Неявные преобразования указателей
• В операциях присваивания и сравненияцелочисленное константное выражение со
значением 0 автоматически преобразуется в
указатель любого типа
• Результатом преобразования 0 в указатель
является NULL, отличный от всех остальных
указателей
– NULL не соответствует ни одной ячейке памяти
41. Неявные преобразования указателей
• В операциях присваивания и сравненияуказатель на значение типа T
автоматически преобразуется в указатель
на значение типа const T и на значение типа
volatile T
42. Неявные преобразования указателей
• Указатель типа void * автоматическипреобразуется к указателю любого типа
– Если результат подвергнуть явному обратному
преобразованию, то мы получим прежний
указатель
• Значение типа функция автоматически
преобразуется к типу указатель на функцию
• Значение типа указатель на функцию
автоматически преобразуется к типу
функция
43. Явные преобразования указателей
Для указателей допускаются и другие преобразования, но в связи с ними возникает проблема зависимости
результата от реализации. Эти преобразования должны быть специфицированы явным оператором
преобразования типа или оператором приведения
Указатель можно привести к целочисленному типу, достаточно большому для его хранения; требуемый
размер зависит от реализации. Функция преобразования также зависит от реализации
Объект целочисленного типа можно явно преобразовать в указатель. Если целое получено из указателя и
имеет достаточно большой размер, это преобразование даст тот же указатель; в противном случае
результат зависит от реализации
Указатель на один тип можно преобразовать в указатель на другой тип. Если исходный указатель ссылается
на объект, должным образом не выровненный по границам слов памяти, то в результате может произойти
ошибка адресации. Если требования на выравнивание у нового типа меньше или совпадают с
требованиями на выравнивание первоначального типа, то гарантируется, что преобразование указателя в
другой тип и обратно его не изменит; понятие "выравнивание" зависит от реализации, однако в любой
реализации объекты типа char предъявляют минимальные требования на выравнивание.
Указатель может быть преобразован в другой указатель того же типа с добавлением или удалением
квалификаторов того типа объекта, на который этот указатель показывает. Новый указатель, полученный
добавлением квалификатора, имеет то же значение, но с дополнительными ограничениями, внесенными
новыми квалификаторами. Операция по удалению квалификатора у объекта приводит к тому, что
восстанавливается действие его начальных квалификаторов, заданных в объявлении этого объекта.
Наконец, указатель на функцию может быть преобразован в указатель на функцию другого типа. Вызов
функции по преобразованному указателю зависит от реализации; однако, если указатель еще раз
преобразовать к его исходному типу, результат будет идентичен вызову по первоначальному указателю.
44. Заключение
• Выражения и подвыражения• Классы, приоритеты и ассоциативность
операций языка Си
• Выражения l-value
• Порядок вычисления выражений, точки
следования, побочные эффекты
• Неявные преобразования типов