Язык программирования Си
Базовый набор инструментов
Содержание
2
Функции
Основные элементы программы - фрагменты кода, исполняющиеся последовательно от входа в функцию до выхода из неё
3
Функции
Основные элементы программы - фрагменты кода, исполняющиеся последовательно от входа в функцию до выхода из неё
Функции могут вызывать друг друга, передавать и принимать параметры, возвращать значения - всё это будет позже
4
Функции
Основные элементы программы - фрагменты кода, исполняющиеся последовательно от входа в функцию до выхода из неё
Функции могут вызывать друг друга, передавать и принимать параметры, возвращать значения - всё это будет позже
Скомпилированный код функций находится в адресном пространстве программы - это факт будет очень важен в дальнейшем
5
Функция main
В базовом наборе инструментов нам достаточно одной функции с именем main, возвращающей значение типа int
6
Функция main
В базовом наборе инструментов нам достаточно одной функции с именем main, возвращающей значение типа int
Это функция, которую позовёт операционная система, создав адресное пространство для нашей программы и загрузив её в него
Это не совсем так, но мы будем работать именно в такой модели (подобных допущений в дальнейшем будет много)
7
Функция main
В базовом наборе инструментов нам достаточно одной функции с именем main, возвращающей значение типа int
Это функция, которую позовёт операционная система, создав адресное пространство для нашей программы и загрузив её в него
Это не совсем так, но мы будем работать именно в такой модели (подобных допущений в дальнейшем будет много)
Возвращаемое значение будет получено операционной системой и может быть использовано, как признак успешного (как правило, 0) или неуспешного (не 0) завершения работы
8
Минимальная программа на языке Си
9
int main() {
return 0;
}
Минимальная программа на языке Си
10
int main() {
return 0;
}
Тип возвращаемого значения
Минимальная программа на языке Си
11
int main() {
return 0;
}
Тип возвращаемого значения
Имя функции
Минимальная программа на языке Си
12
int main() {
return 0;
}
Тип возвращаемого значения
Имя функции
Список параметров (пустой)
Минимальная программа на языке Си
13
int main() {
return 0;
}
Тип возвращаемого значения
Имя функции
Список параметров (пустой)
Тело функции
Прототип функции
Тип возвращаемого значения, имя и список типов параметров образуют прототип функции
14
Тело функции
Список операторов, заключённый в фигурные скобки, написанный после прототипа, образует тело функции
15
Тело функции
Список операторов, заключённый в фигурные скобки, написанный после прототипа, образует тело функции
Выполнение функции - это выполнение её тела сверху вниз, пока не встретится оператор выхода из функции
16
Оператор выхода из функции
17
return expr;
Оператор выхода из функции
18
return expr;
Произвольное выражение, результат вычисления которого является возвращаемым значением
Оператор выхода из функции
Может быть написан в любом месте тела функции
19
Оператор выхода из функции
Может быть написан в любом месте тела функции
При его исполнении происходит немедленный выход из функции с возвратом указанного возвращаемого значения, дальнейшее тело функции не выполняется
20
Оператор выхода из функции
Может быть написан в любом месте тела функции
При его исполнении происходит немедленный выход из функции с возвратом указанного возвращаемого значения, дальнейшее тело функции не выполняется
21
int main() {
operator1;
operator2;
return 0;
operator3;
operator4;
}
Оператор выхода из функции
Может быть написан в любом месте тела функции
При его исполнении происходит немедленный выход из функции с возвратом указанного возвращаемого значения, дальнейшее тело функции не выполняется
22
int main() {
operator1;
operator2;
return 0;
operator3;
operator4;
}
Объекты
23
Объекты
Объекты - это данные, которые читают, модифицируют и записывают функции программы в процессе своего исполнения
24
Объекты
Объекты - это данные, которые читают, модифицируют и записывают функции программы в процессе своего исполнения
Существует три вида объектов:
25
Объекты
Объекты - это данные, которые читают, модифицируют и записывают функции программы в процессе своего исполнения
Существует три вида объектов:
26
Переменные
Участки памяти, к которым в программе обращаются по именам
27
Переменные
Участки памяти, к которым в программе обращаются по именам
Свойства: тип, имя, область видимости, тип хранения, время жизни
28
Переменные
Участки памяти, к которым в программе обращаются по именам
Свойства: тип, имя, область видимости, тип хранения, время жизни
Операции: чтение, запись
29
Имена
Должны быть уникальными в одной и то же области видимости
30
Имена
Должны быть уникальными в одной и то же области видимости
Имеют значение только для исходного кода, в скомпилированном машинном пропадают
Это не полная правда, имена остаются и используются для связи нескольких скомпилированных по отдельностей частей одной программы
31
Имена
Должны быть уникальными в одной и то же области видимости
Имеют значение только для исходного кода, в скомпилированном машинном пропадают
Это не полная правда, имена остаются и используются для связи нескольких скомпилированных по отдельностей частей одной программы
Имена переменных (как и прочие идентификаторы в языке Си) могут содержать буквы, цифры и подчерк; должны начинаться с букв или подчерка
32
Область видимости
Область видимости переменной (scope) - участок программы, в котором переменная может быть использована (записана или прочитана)
33
Область видимости
Область видимости переменной (scope) - участок программы, в котором переменная может быть использована (записана или прочитана)
Существуют четыре вида области видимости:
34
Область видимости
Область видимости переменной (scope) - участок программы, в котором переменная может быть использована (записана или прочитана)
Существуют четыре вида области видимости:
35
Тип хранения
Тип хранения (storage duration) - способ хранения переменной, по которому определяется её время жизни
36
Тип хранения
Тип хранения (storage duration) - способ хранения переменной, по которому определяется её время жизни
Есть три типа хранения:
37
Тип хранения
Тип хранения (storage duration) - способ хранения переменной, по которому определяется её время жизни
Есть три типа хранения:
38
Время жизни
Время жизни (lifetime) - период исполнения программы, во время которого для переменной гарантируется:
39
Время жизни
Время жизни (lifetime) - период исполнения программы, во время которого для переменной гарантируется:
Для статического типа хранения время жизни определяется, как всё время исполнения программы
40
TL;DR
Область видимости, тип хранения и время жизни - очень важные понятия, не такие простые, как кажутся на первый взгляд; мы будем периодически возвращаться к ним
41
TL;DR
Область видимости, тип хранения и время жизни - очень важные понятия, не такие простые, как кажутся на первый взгляд; мы будем периодически возвращаться к ним
А пока (в базовом наборе, для глобальных переменных) всё просто:
42
Глобальные переменные
43
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Глобальные переменные
44
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Тип переменной
Глобальные переменные
45
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Тип переменной
Имя переменной
Глобальные переменные
46
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Тип переменной
Имя переменной
Определение переменной
Глобальные переменные
47
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Тип переменной
Имя переменной
Записи (присваивания) переменной
Определение переменной
Глобальные переменные
48
int x;
int main(args) {
x = 42;
x = x + 37;
return 0;
}
Тип переменной
Имя переменной
Определение переменной
Записи (присваивания) переменной
Чтение переменной
Определение переменных
Можно определить несколько переменных через запятую (у них будет один и тот же тип)
49
Определение переменных
Можно определить несколько переменных через запятую (у них будет один и тот же тип)
Каждой переменной можно присвоить начальное значение (initial value)
50
Определение переменных
Можно определить несколько переменных через запятую (у них будет один и тот же тип)
Каждой переменной можно присвоить начальное значение (initial value)
51
int x = 42, y = 37, z;
Инициализация глобальных переменных
Начальное значение глобальной переменной должно вычисляться константным выражением (constant expression) - не содержать использований переменных, вызовов функций и некоторых других конструкций
52
Инициализация глобальных переменных
Начальное значение глобальной переменной должно вычисляться константным выражением (constant expression) - не содержать использований переменных, вызовов функций и некоторых других конструкций
Начальное значение по умолчанию равно 0
53
Инициализация глобальных переменных
Начальное значение глобальной переменной должно вычисляться константным выражением (constant expression) - не содержать использований переменных, вызовов функций и некоторых других конструкций
Начальное значение по умолчанию равно 0
Начальные значения присваиваются один раз перед началом работы функции main
54
Объявление перед использованием
В языке Си действует правило, что любая сущность должна быть объявлена в исходном коде до того, как будет использована
Это связано с устаревшими ограничениями на эффективность работы компилятора и неактуально для современных языков
55
Объявление перед использованием
В языке Си действует правило, что любая сущность должна быть объявлена в исходном коде до того, как будет использована
Это связано с устаревшими ограничениями на эффективность работы компилятора и неактуально для современных языков
56
Не определена, а именно объявлена; это разные термины, но об этом позже
Объявление перед использованием
В языке Си действует правило, что любая сущность должна быть объявлена в исходном коде до того, как будет использована
Это связано с устаревшими ограничениями на эффективность работы компилятора и неактуально для современных языков
Если вы объявите переменные текстуально после функции main, вы не сможете их использовать
57
Не определена, а именно объявлена; это разные термины, но об этом позже
58
int main() {
x = 37;
return 0;
}
int x = 42;
59
int main() {
x = 37;
return 0;
}
int x = 42;
main.c: In function ‘main’:
main.c:2:5: error: ‘x’ undeclared (first use in this function)
2 | x = 37;
| ^
Вывод данных
В языке Си принят вывод данных по формату (formatted output) - функции вывода принимают:
60
Вывод данных
В языке Си принят вывод данных по формату (formatted output) - функции вывода принимают:
61
Вывод данных
В языке Си принят вывод данных по формату (formatted output) - функции вывода принимают:
62
Вывод данных
В языке Си принят вывод данных по формату (formatted output) - функции вывода принимают:
63
Вывод данных
В языке Си принят вывод данных по формату (formatted output) - функции вывода принимают:
64
Вывод данных
65
#include <stdio.h>
int main() {
printf("Hello, world!");
return 0;
}
Вывод данных
66
#include <stdio.h>
int main() {
printf("Hello, world!");
return 0;
}
Подключаем библиотеку
Вывод данных
67
#include <stdio.h>
int main() {
printf("Hello, world!");
return 0;
}
print formatted - печать по формату
функция, печатающая в стандартный поток вывода (если по простому, на экран)
Подключаем библиотеку
printf
68
printf("...", ...);
Строка формата - может содержать обычный текст, эскейп последовательности и спецификаторы формата
Список аргументов, которые будут подставлены вместо спецификаторов формата, в порядке их указания
printf
69
printf("...", ...);
Список аргументов, которые будут подставлены вместо спецификаторов формата, в порядке их указания
printf("Hello, world!");
Строка формата - может содержать обычный текст, эскейп последовательности и спецификаторы формата
printf
70
printf("...", ...);
Список аргументов, которые будут подставлены вместо спецификаторов формата, в порядке их указания
printf("Hello, world!");
printf("Hello, world!\n");
Строка формата - может содержать обычный текст, эскейп последовательности и спецификаторы формата
printf
71
printf("...", ...);
Список аргументов, которые будут подставлены вместо спецификаторов формата, в порядке их указания
printf("Hello, world!");
printf("Hello, world!\n");
printf("value = %d", value);
Строка формата - может содержать обычный текст, эскейп последовательности и спецификаторы формата
printf
72
printf("...", ...);
Список аргументов, которые будут подставлены вместо спецификаторов формата, в порядке их указания
printf("Hello, world!");
printf("Hello, world!\n");
printf("value = %d", value);
Строка формата - может содержать обычный текст, эскейп последовательности и спецификаторы формата
printf
73
#include <stdio.h>
int x, y;
int main() {
x = 42;
y = 37;
printf("x = %d\ny = %d", x, y);
return 0;
}
printf
74
#include <stdio.h>
int x, y;
int main() {
x = 42;
y = 37;
printf("x = %d\ny = %d", x, y);
return 0;
}
printf
75
#include <stdio.h>
int x, y;
int main() {
x = 42;
y = 37;
printf("x = %d\ny = %d", x, y);
return 0;
}
> x = 42
> y = 37
Эскейп последовательности
Нужны для того, чтобы выводить символы, которые нельзя просто так написать в строке формата
76
Эскейп последовательности
Нужны для того, чтобы выводить символы, которые нельзя просто так написать в строке формата
77
Эскейп последовательность | Символ в выводе |
\n | перевод строки |
\t | горизонтальная табуляция |
\’ | ’ |
\” | ” |
\\ | \ |
Спецификаторы формата
Определяют тип, по которому будет выводиться следующий аргумент, и детали вывода, например, систему счисления, в которой нужно отобразить число
78
Спецификаторы формата
Определяют тип, по которому будет выводиться следующий аргумент, и детали вывода, например, систему счисления, в которой нужно отобразить число
В базовом наборе нам будет достаточно только одного спецификатора %d - вывод числа типа int в десятичной системе счисления
79
Спецификаторы формата
Определяют тип, по которому будет выводиться следующий аргумент, и детали вывода, например, систему счисления, в которой нужно отобразить число
В базовом наборе нам будет достаточно только одного спецификатора %d - вывод числа типа int в десятичной системе счисления
В дальнейшем мы будем добавлять спецификаторы по мере необходимости
80
Ввод данных
Похож на вывод - строка формата и список переменных, в которые нужно записать значения, прочитанные из потока ввода (в базовом наборе - с клавиатуры)
81
Ввод данных
Похож на вывод - строка формата и список переменных, в которые нужно записать значения, прочитанные из потока ввода (в базовом наборе - с клавиатуры)
Очень важное отличие - к каждой переменной должен быть применён оператор взятия адреса
Что это за оператор, и зачем он должен быть применён - через пару лекций
82
Ввод данных
Похож на вывод - строка формата и список переменных, в которые нужно записать значения, прочитанные из потока ввода (в базовом наборе - с клавиатуры)
Очень важное отличие - к каждой переменной должен быть применён оператор взятия адреса
Что это за оператор, и зачем он должен быть применён - через пару лекций
83
scanf("%d", &x);
Ввод данных
Похож на вывод - строка формата и список переменных, в которые нужно записать значения, прочитанные из потока ввода (в базовом наборе - с клавиатуры)
Очень важное отличие - к каждой переменной должен быть применён оператор взятия адреса
Что это за оператор, и зачем он должен быть применён - через пару лекций
84
scanf("%d", &x);
Ввод данных и разделители
Ввод с клавиатуры в консоль устроен таким образом, что вы можете вводить текст, пока не встретится символ перевода строки или признак конца файла
C клавиатуры можно ввести признак конца файла, потому что она - файл
85
Ввод данных и разделители
Ввод с клавиатуры в консоль устроен таким образом, что вы можете вводить текст, пока не встретится символ перевода строки или признак конца файла
C клавиатуры можно ввести признак конца файла, потому что она - файл
После того, как текст введён, scanf вытаскивает из него для чтения символы только до первого разделителя
Разделителями являются символы пробела, перевода строки, горизонтальной табуляции и признак конца файла
86
Ввод данных и разделители
Ввод с клавиатуры в консоль устроен таким образом, что вы можете вводить текст, пока не встретится символ перевода строки или признак конца файла
C клавиатуры можно ввести признак конца файла, потому что она - файл
После того, как текст введён, scanf вытаскивает из него для чтения символы только до первого разделителя
Разделителями являются символы пробела, перевода строки, горизонтальной табуляции и признак конца файла
Все набранные символы будут сохранены и использованы в дальнейших вызовах scanf функций
87
Ввод данных и разделители
Там, где может быть один разделитель, может быть сколько угодно - все они будут пропущены очередным scanf
88
Ввод данных и разделители
Там, где может быть один разделитель, может быть сколько угодно - все они будут пропущены очередным scanf
Не очень хорошей идеей является вызывать scanf для чтения больше, чем одной переменной - в таком случае символы между спецификаторами формата трактуются особым образом
89
Ввод данных и разделители
Там, где может быть один разделитель, может быть сколько угодно - все они будут пропущены очередным scanf
Не очень хорошей идеей является вызывать scanf для чтения больше, чем одной переменной - в таком случае символы между спецификаторами формата трактуются особым образом
В целом scanf довольно неудобный инструмент для чтения текстовых данных - в базовом наборе нам его будет достаточно, далее изучим другие средства
90
Ввод-вывод данных
Все практические задачи оформлены так, что входные данные вводятся с клавиатуры, а выходные выводятся на консоль
91
Ввод-вывод данных
Все практические задачи оформлены так, что входные данные вводятся с клавиатуры, а выходные выводятся на консоль
Для простоты тестирования научитесь в собственном окружении перенаправлять потоки ввода-вывода программы из и в файлы (система тестирования делает именно это)
Этому (и многому другому) вас научат в курсе ИСП
92
Операторы (operators)
Каждый оператор характеризуется:
93
Операторы (operators)
Каждый оператор характеризуется:
Самое распространённое поведение, касающееся типов:
94
Бинарные арифметические операторы
95
Оператор | Действие |
+ | сложение |
- | вычитание |
* | умножение |
/ | целая часть от деления |
% | остаток от деления |
Унарные арифметические операторы
96
Оператор | Действие |
- | унарный минус |
+ | унарный плюс |
x++ | постинкремент |
x–- | постдекремент |
++x | преинкремент |
--x | предекремент |
Пост- и пре- инкременты
Оба увеличивают на 1 переменную, к которой применяются
97
Пост- и пре- инкременты
Оба увеличивают на 1 переменную, к которой применяются
Оба возвращают результат:
98
Пост- и пре- инкременты
Оба увеличивают на 1 переменную, к которой применяются
Оба возвращают результат:
Декременты то же самое, только уменьшают на 1
99
Пост- и пре- инкременты
100
x = 0;
y = x++;
x = 0;
y = ++x;
x: 1
x: 1
Пост- и пре- инкременты
101
x = 0;
y = x++;
x = 0;
y = ++x;
x: 1 y: 0
x: 1 y: 1
Переполнение арифметических операторов
Для беззнаковых типов - результат определяется вычислением по модулю
102
Переполнение арифметических операторов
Для беззнаковых типов - результат определяется вычислением по модулю
Для знаковых типов - UB
103
Переполнение арифметических операторов
Для беззнаковых типов - результат определяется вычислением по модулю
Для знаковых типов - UB
Применяя операторы к операндам неизвестной природы (например, введённых пользователям), вы, согласно стандарту, должны перед операцией проверить, не случится ли переполнение
В нашем курсе мы договорились это игнорировать; 3-е домашнее задание убедит вас, что это разумное решение
104
Логические операторы
105
Оператор | Действие |
&& | Конъюнкция |
|| | Дизъюнкция |
! | Отрицание |
Логические операторы
Принимают любые скалярные типы (scalar types) - целые, вещественные и указатели (скоро будут)
Аргументы трактуются как 0 (ложь) и не-ноль (истина) - любое значение, отличающееся от 0
106
Логические операторы
Принимают любые скалярные типы (scalar types) - целые, вещественные и указатели (скоро будут)
Аргументы трактуются как 0 (ложь) и не-ноль (истина) - любое значение, отличающееся от 0
Возвращают значение типа int: 0 (ложь) или 1 (истина)
107
Логические операторы
Принимают любые скалярные типы (scalar types) - целые, вещественные и указатели (скоро будут)
Аргументы трактуются как 0 (ложь) и не-ноль (истина) - любое значение, отличающееся от 0
Возвращают значение типа int: 0 (ложь) или 1 (истина)
Некоторые компиляторы раньше нарушали это правило и возвращали 0 (ложь) и не-ноль (истина)
Из-за этого при программировании на языке Си не принято сравнивать результат логической операции с 1; вместо этого сравнение инвертируется и результат сравнивается с 0
108
Ленивые вычисления
Бинарные логические операторы обладают свойством ленивых вычислений - если первый операнд однозначно определяет результат операции, второй аргумент не вычисляется
Если второй операнд - переменная, это ни на что не влияет, но если это сложное выражение (содержит вызовы функций или побочные эффекты), поведение программы меняется
109
Ленивые вычисления
Бинарные логические операторы обладают свойством ленивых вычислений - если первый операнд однозначно определяет результат операции, второй аргумент не вычисляется
Если второй операнд - переменная, это ни на что не влияет, но если это сложное выражение (содержит вызовы функций или побочные эффекты), поведение программы меняется
&& - если первый операнд равен 0, результат гарантированно 0, второй операнд не вычисляется
110
Ленивые вычисления
Бинарные логические операторы обладают свойством ленивых вычислений - если первый операнд однозначно определяет результат операции, второй аргумент не вычисляется
Если второй операнд - переменная, это ни на что не влияет, но если это сложное выражение (содержит вызовы функций или побочные эффекты), поведение программы меняется
&& - если первый операнд равен 0, результат гарантированно 0, второй операнд не вычисляется
|| - если первый операнд не равен 0, результат гарантированно 1, второй операнд не вычисляется
111
Битовые операторы
112
Оператор | Действие |
& | Битовая конъюнкция |
| | Битовая дизюнкция |
^ | Битовое исключающее ИЛИ (XOR) |
<< | Битовый сдвиг вправо |
>> | Битовый сдвиг влево |
~ | Битовое отрицание |
Битовые операторы
Принимают любые целые типы, возвращают целый тип по правилу арифметических операций (самый широкий тип из операндов + расширение до int)
Для битовых сдвигов тип результата равен типу первого операнда, расширенному до int
113
Битовые операторы
Принимают любые целые типы, возвращают целый тип по правилу арифметических операций (самый широкий тип из операндов + расширение до int)
Для битовых сдвигов тип результата равен типу первого операнда, расширенному до int
&, |, ^, ~ - совершают простое логическое действие над каждой парой битов своих операндов
114
Битовые операторы
Принимают любые целые типы, возвращают целый тип по правилу арифметических операций (самый широкий тип из операндов + расширение до int)
Для битовых сдвигов тип результата равен типу первого операнда, расширенному до int
&, |, ^, ~ - совершают простое логическое действие над каждой парой битов своих операндов
Не обладают свойством ленивых вычислений
При этом результат | может быть использован, как результат || (кроме сравнения его с 1, что и так не стоит делать)
115
Битовые операторы
116
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
Битовые операторы
117
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
||
Битовые операторы
118
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
||
0
0
0
0
0
0
0
1
Битовые операторы
119
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
||
0
0
0
0
0
0
0
1
|
Битовые операторы
120
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
||
0
0
0
0
0
0
0
1
|
1
0
1
1
1
0
1
1
Битовые операторы
121
1
0
0
1
0
0
1
1
0
0
1
1
1
0
0
1
||
0
0
0
0
0
0
0
1
|
1
0
1
1
1
0
1
1
Результаты | и || всегда либо оба равны 0, либо оба не равны. Таким образом операцию | можно использовать вместо ||, если вам нужно, чтобы второй операнд всегда вычислялся, даже если первый уже не 0. Например, это может быть вызов функции с побочными эффектами, которые вам действительно нужны.
Битовый сдвиг влево
122
0
0
0
1
0
0
1
1
Битовый сдвиг влево
123
0
0
0
1
0
0
1
1
<< 2
0
1
0
0
1
1
0
0
Битовый сдвиг влево
124
0
0
0
1
0
0
1
1
<< 2
0
1
0
0
1
1
0
0
Битовый сдвиг числа X влево на N
N старших битов числа X отбрасываются, остальные перемещаются влево на N, справа дописываются N нулевых битов
125
Битовый сдвиг числа X влево на N
N старших битов числа X отбрасываются, остальные перемещаются влево на N, справа дописываются N нулевых битов
X << N равен X * 2N, при этом вычисляется эффективнее
Не надо писать самостоятельно сдвиги ради эффективности, любой компилятор сделает это за вас
126
Битовый сдвиг числа X влево на N
N старших битов числа X отбрасываются, остальные перемещаются влево на N, справа дописываются N нулевых битов
X << N равен X * 2N, при этом вычисляется эффективнее
Не надо писать самостоятельно сдвиги ради эффективности, любой компилятор сделает это за вас
UB:
127
Битовый сдвиг вправо
То же самое, только вправо, и результат равен результату деления на 2N. Отличия:
128
Битовый сдвиг вправо
То же самое, только вправо, и результат равен результату деления на 2N. Отличия:
В нашей платформе на место освободившихся битов копируется старший бит числа (соответствует дополнительному коду)
129
Битовый сдвиг вправо
130
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
Битовый сдвиг вправо
131
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
1
1
1
1
1
1
...
Битовый сдвиг вправо
132
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
?
?
?
1
1
1
...
Битовый сдвиг вправо
133
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
1
1
1
1
1
1
...
Старший (знаковый) бит
Битовый сдвиг вправо
134
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
1
1
1
1
1
1
...
Старший (знаковый) бит
Его копии
Битовый сдвиг вправо
135
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
1
1
1
1
1
1
...
Битовый сдвиг вправо
136
#include <stdio.h>
int main() {
int x = -1;
x = x >> 3;
printf("%d\n", x);
return 0;
}
1
1
1
1
1
1
...
> -1
Битовый сдвиг вправо
То, что -1, сдвинутое вправо на N (поделённое на 2N), остаётся -1, конечно, немного странно
Зато с остальными отрицательными числами получается всё удачно; например, -4 >> 1 = -2
137
Операторы присваивания
138
Оператор | Действие |
= | Присваивание |
| |
| |
Операторы присваивания
139
Оператор | Действие |
= | Присваивание |
+= | Сложение и присваивание |
... | ... |
Операторы присваивания
140
Оператор | Действие |
= | Присваивание |
+= | Сложение и присваивание |
... | ... |
Оператор присваивания можно комбинировать с любым бинарным оператором
lvalue
Левый операнд оператора присваивания должен быть изменяемым (modifiable) lvalue - выражением, являющимся объектом, который можно изменить
TL;DR - переменной, но на самом деле, всё намного сложнее
141
lvalue
Левый операнд оператора присваивания должен быть изменяемым (modifiable) lvalue - выражением, являющимся объектом, который можно изменить
TL;DR - переменной, но на самом деле, всё намного сложнее
Интуитивно понятное явление - нельзя пытаться присваивать в константы, результаты других выражений, неизменяемые объекты и так далее
142
Операторы присваивания возвращают значение!
143
a = b = c = 42;
Операторы присваивания возвращают значение!
144
a = (b = (c = 42));
Операторы сравнения и равенства
145
Оператор | Действие |
== | Сравнение на равенство |
!= | Сравнение на неравенство |
< | Сравнение на меньше |
> | Сравнение на больше |
<= | Сравнение на меньше или равно |
>= | Сравнение на больше или равно |
Операторы сравнения и равенства
146
Оператор | Действие |
== | Сравнение на равенство |
!= | Сравнение на неравенство |
< | Сравнение на меньше |
> | Сравнение на больше |
<= | Сравнение на меньше или равно |
>= | Сравнение на больше или равно |
Возвращают значение типа int: 0 (ложь) или 1 (истина)
Ура!
Теперь вы можете решить второе домашнее задание: калькулятор, в котором примените все изученные инструменты
147
Условный оператор
148
a ? b : c;
Если a - истина, возвращает b, иначе - c
Условный оператор
Обладает свойством ленивых вычислений - тот операнд, который не возвращается, не вычисляется вовсе
149
Условный оператор
Обладает свойством ленивых вычислений - тот операнд, который не возвращается, не вычисляется вовсе
Часто называется тернарным, потому что условным оператором называют if statement, но это неправильно (тернарный - это то же самое, что бинарный и унарный)
150
Условный оператор
Обладает свойством ленивых вычислений - тот операнд, который не возвращается, не вычисляется вовсе
Часто называется тернарным, потому что условным оператором называют if statement, но это неправильно (тернарный - это то же самое, что бинарный и унарный)
Этим оператором нельзя злоупотреблять
151
(x % 400) ? 1 : ((x % 100) ? 0 : !(x % 4))
Оператор приведения (cast)
152
int x = ...;
... = (unsigned char) x;
Возвращает значение x, преобразованное к типу unsigned char
Operator, expression, statement
Основные элементы текста программы (ещё есть объявления и определения)
153
Operator, expression, statement
Основные элементы текста программы (ещё есть объявления и определения)
Выражение (expression) - композиция оператора и операндов, которые тоже могут быть выражениями
154
Operator, expression, statement
Основные элементы текста программы (ещё есть объявления и определения)
Выражение (expression) - композиция оператора и операндов, которые тоже могут быть выражениями
… (statement) - действие, которое должно быть выполнено
155
Operator, expression, statement
Основные элементы текста программы (ещё есть объявления и определения)
Выражение (expression) - композиция оператора и операндов, которые тоже могут быть выражениями
Оператор (statement) - действие, которое должно быть выполнено
Придётся пользоваться де-факто стандартным переводом, хоть он и конфликтует с переводом термина operator
156
Условный оператор (statement)
157
code A
if (condition) {
code B
} else {
code C
}
code D
Условный оператор (statement)
158
code A
if (condition) {
code B
} else {
code C
}
code D
Условный оператор (statement)
159
code A
if (condition) {
code B
} else {
code C
}
code D
Условный оператор (statement)
160
code A
if (condition) {
code B
} else {
code C
}
code D
Если condition - истина
Если condition - ложь
Условный оператор (statement)
161
code A
if (condition) {
code B
} else {
code C
}
code D
Условный оператор (statement)
162
code A
if (condition) {
code B
}
code D
Можно писать без else части, если она не нужна по семантике
Условный оператор (statement)
163
code A
if (condition)
code B
code D
И без фигурных скобок, если code B - один оператор (statement), но этого обычно лучше избегать
Оператор (statement) цикла while
164
while (condition) {
code A
}
code B
Оператор (statement) цикла while
165
while (condition) {
code A
}
code B
Оператор (statement) цикла while
166
while (condition) {
code A
}
code B
Если condition - истина
Если condition - ложь
Оператор (statement) цикла while
167
while (condition) {
code A
}
code B
Если condition - истина
Оператор (statement) цикла while
168
while (condition) {
code A
}
code B
Если condition - истина
Если condition - ложь
Оператор (statement) цикла while
169
while (condition) {
code A
}
code B
Если condition - истина
Оператор (statement) цикла while
170
while (condition) {
code A
}
code B
Если condition - истина
Если condition - ложь
Оператор (statement) цикла while
171
while (condition) {
code A
}
code B
Если condition - истина
Если condition - ложь
И так далее
Оператор (statement) цикла do-while
172
do {
code A
} while (condition);
code B
Оператор (statement) цикла do-while
173
do {
code A
} while (condition);
code B
Оператор (statement) цикла do-while
174
do {
code A
} while (condition);
code B
Оператор (statement) цикла do-while
175
do {
code A
} while (condition);
code B
Оператор (statement) цикла do-while
176
do {
code A
} while (condition);
code B
Если condition - истина
Если condition - ложь
И так далее
Разница между циклами while и do-while
Тело цикла do-while гарантированно исполнится хотя бы один раз
177
Разница между циклами while и do-while
Тело цикла do-while гарантированно исполнится хотя бы один раз
В большинстве случаев на практике цикл while более применимый, но никогда не забывайте о существовании другого
178
Операторы (statement) break и continue
179
while (...) {
...
if (condition) {
continue;
}
...
if (condition) {
break;
}
...
}
code A
Операторы (statement) break и continue
180
while (...) {
...
if (condition) {
continue;
}
...
if (condition) {
break;
}
...
}
code A
Операторы (statement) break и continue
181
while (...) {
...
if (condition) {
continue;
}
...
if (condition) {
break;
}
...
}
code A
Операторы (statement) break и continue
Любой цикл всегда можно написать без их использования, но часто они позволяют сделать более понятный и простой код
182
Вывести числа от 1 до N через пробел
183
int N, i;
...
i = 1;
while (i <= N) {
printf("%d ", i);
i++;
}
Вывести числа от 1 до N через пробел
184
int N, i;
...
i = 1;
while (i <= N) {
printf("%d ", i);
i++;
}
Этот символ попадёт в вывод и после последнего числа
Вывести числа от 1 до N через пробел
185
int N, i;
...
i = 1;
while (i <= N) {
printf("%d", i);
if (i != N) {
printf(" ");
}
i++;
}
Вывести числа от 1 до N через пробел
186
int N, i;
...
i = 1;
while (i <= N) {
printf("%d", i);
if (i != N) {
printf(" ");
}
i++;
}
Начальное значение
Вывести числа от 1 до N через пробел
187
int N, i;
...
i = 1;
while (i <= N) {
printf("%d", i);
if (i != N) {
printf(" ");
}
i++;
}
Начальное значение
Условие продолжения
Вывести числа от 1 до N через пробел
188
int N, i;
...
i = 1;
while (i <= N) {
printf("%d", i);
if (i != N) {
printf(" ");
}
i++;
}
Начальное значение
Условие продолжения
Шаг цикла
Оператор (statement) цикла for
189
int N, i;
...
for (i = 1; i <= N; i++) {
printf("%d", i);
if (i != N) {
printf(" ");
}
}
Оператор (statement) цикла for
Собранные вместе выражения начального значения, условия продолжения и шага цикла читаются намного лучше, чем когда они разбросаны по коду
190
Оператор (statement) цикла for
Собранные вместе выражения начального значения, условия продолжения и шага цикла читаются намного лучше, чем когда они разбросаны по коду
Как правило затрагивают одну переменную, называемую индуктивной
191
Оператор (statement) цикла for
Собранные вместе выражения начального значения, условия продолжения и шага цикла читаются намного лучше, чем когда они разбросаны по коду
Как правило затрагивают одну переменную, называемую индуктивной
Избегайте изменений индуктивной переменной внутри цикла for, такое лучше либо переписать на обычный цикл while, либо обойтись как-то иначе
192