Операторы, условия и циклы
Арифметика и присваивание
int x = 10;
x = x + 5; /* 15 */
x += 5; /* 20 — краткая запись */
x -= 3; x *= 2; x /= 4; x %= 3;
Инкремент и декремент
int i = 5;
int a = i++; /* постфиксный: a = 5, затем i = 6 */
int b = ++i; /* префиксный: i = 7, затем b = 7 */
Где это встретится
В задаче 8 экзамена есть строка mem->counter++; — увеличение счётчика в разделяемой памяти. Хотя это одна строка C, процессор выполняет её за три шага: прочитать, прибавить, записать. Если два процесса делают это одновременно, одно увеличение может потеряться. Это называется гонка — разберём в модуле о синхронизации.
Сравнения и логика
| Оператор | Смысл |
|---|---|
==, != |
равно, не равно |
<, <=, >, >= |
сравнения |
&& |
логическое И |
\|\| |
логическое ИЛИ |
! |
логическое НЕ |
В C нет отдельного логического типа (в старом стандарте): 0 — ложь, всё остальное — истина. Поэтому пишут if (ptr) вместо if (ptr != NULL).
&& и || вычисляются лениво: если результат ясен по левой части, правая не вычисляется.
if (p != NULL && p->state == 0) { ... } /* безопасно: при p == NULL правая часть не выполнится */
Побитовые операторы
| Оператор | Действие | Пример (в двоичном) |
|---|---|---|
& |
И | 1100 & 1010 = 1000 |
\| |
ИЛИ | 1100 \| 1010 = 1110 |
^ |
исключающее ИЛИ | 1100 ^ 1010 = 0110 |
~ |
инверсия | ~0000 = 1111 |
<<, >> |
сдвиги | 1 << 3 = 1000 |
Системное программирование ими пропитано:
/* комбинирование флагов */
mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
/* проверка флага */
if (flags & MAP_SHARED) { ... }
/* сброс флага */
flags &= ~MAP_SHARED;
/* разбор виртуального адреса: номер страницы и смещение при странице 4096 байт */
unsigned long page = addr >> 12; /* делим на 2^12 */
unsigned long offset = addr & 0xFFF; /* младшие 12 бит */
Почему сдвиг вместо деления?
Размер страницы — степень двойки. Деление на 2ᵏ — это сдвиг вправо на k бит, а остаток от деления на 2ᵏ — младшие k бит (& (2ᵏ − 1)). Аппаратура MMU именно так и «режет» адрес на номер страницы и смещение: просто берёт нужные провода-биты.
Условия
if (pid == -1) {
perror("fork");
_exit(1);
} else if (pid == 0) {
/* код потомка */
} else {
/* код родителя: pid > 0 */
}
switch удобен для выбора по целому значению:
switch (sig) {
case SIGINT:
printf("Ctrl+C\n");
break; /* без break выполнение «провалится» в следующий case */
case SIGTERM:
case SIGQUIT:
printf("завершение\n");
break;
default:
printf("другой сигнал\n");
}
Тернарный оператор: int max = (a > b) ? a : b;
Циклы
for (int i = 0; i < 70; i++) { /* инициализация; условие; шаг */
sum += a[i] * b[i];
}
while (n > 0) { /* проверка до тела */
n--;
}
do { /* тело хотя бы один раз */
c = getchar();
} while (c != '\n');
for (;;) { ... } /* бесконечный цикл; выход — break или return */
break— выйти из ближайшего цикла (илиswitch);continue— перейти к следующей итерации.
Как читать цикл в задаче
Выпишите на бумаге значения переменной цикла и что происходит на каждой итерации. Для циклов с fork рисуйте дерево процессов — у каждого процесса своя копия переменной i.
Итог
i++возвращает старое значение,++i— новое;counter++— это три машинных шага.- 0 — ложь, не 0 — истина;
&&и||ленивые. - Флаги комбинируют
|, проверяют&, сбрасывают&= ~. =— присваивание,==— сравнение.
Практика решено 0 из 6
Что напечатает программа?
int i = 5;
int a = i++;
int b = ++i;
printf("%d %d %d\n", a, b, i);
Сколько раз выполнится тело цикла for (int i = 0; i < 70; i++) { ... }?
Чему равно выражение PROT_READ | PROT_WRITE, если PROT_READ = 1, а PROT_WRITE = 2?
Проверьте, установлен ли во флагах flags бит MAP_SHARED, и если да — напечатайте shared.
Используйте побитовое И: if (flags & MAP_SHARED).
int flags = MAP_SHARED | MAP_ANONYMOUS;
if (flags & MAP_SHARED) {
printf("shared\n");
}
Что произойдёт в этом фрагменте: if (x = 0) printf("zero");?
Сколько строк напечатает цикл?
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) continue;
if (i > 6) break;
printf("%d\n", i);
}
Войдите, чтобы отмечать пройденные уроки.