Выражения языка С
План лекции
Обзор операций языка Си 1/3
Обзор операций языка Си 2/3
Обзор операций языка Си 3/3
Приоритеты операций в языке Си
Приоритеты операций в языке Си
Приоритеты операций в языке Си
Первичные выражения
Доступ к элементу массива
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения и подвыражения
Выражения l-value
Выражения l-value
Выражения l-value
Выражения l-value
Точки следования, побочные эффекты
Точки следования, побочные эффекты
Точки следования, побочные эффекты
Точки следования, побочные эффекты
Точки следования, побочные эффекты
Точки следования, побочные эффекты
Неявные преобразования типов
Неявные преобразования чисел
Неявные преобразования чисел
Неявные преобразования чисел
Неявные преобразования чисел
Неявные преобразования чисел
Неявные преобразования указателей
Неявные преобразования указателей
Неявные преобразования указателей
Явные преобразования указателей
Заключение
109.08K
Категория: ПрограммированиеПрограммирование

Выражения языка С

1. Выражения языка С

лекция 4

2. План лекции

• Классы, приоритеты и ассоциативность
операций языка Си
• Выражения 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

• Пример 1
int 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. Точки следования, побочные эффекты

• Пример 1
while (*p++ != 0 && *q++ != 0) *p = *q;
• Все побочные эффекты *p++ != 0 проявятся
до начала вычисления *q++ != 0
– Правило 1
• Все побочные эффекты *p++ != 0 и *q++ != 0
проявятся до начала вычисления *p = *q
– Правило 3
• Что делает этот цикл while?

32. Точки следования, побочные эффекты

• Пример 2
int A[3] = {1,0,2}, *p = A;
int a = (*p++) ? (*p++) : 0; // чему равно a?
• Точка следования находится после первого *p++
• p уже увеличена на 1 при вычислении второго
*p++

33. Точки следования, побочные эффекты

• Пример 3
int 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
• Порядок вычисления выражений, точки
следования, побочные эффекты
• Неявные преобразования типов
English     Русский Правила