Вносим свои различные изменения в Hello world из прошлой статьи. Изучаем простейшие конструкции что позволят превратить наш алгоритм в рабочий код. И на последок напишем пару небольших программ
В начале был алгоритм
Не хочется вдаваться в формальности, поэтому пройдемся быстро по самому важному.
Код исполняет задачу. Путь достижения этой задачи — это алгоритм
Это явный список шагов при помощи команд понятных исполнителю
Основная фишка программирования — создавать функции (своего рода новые команды). Если они построены правильно, то они сводят одну сложную проблему к комбинации более простых
К примеру, нам нужно купить молоко, но только если оно не дороже 100 рублей. Разобьём это на подзадачи:
- Приготовься к походу в магазин
- Отправься в магазин
- Узнай цену молока
- Если она меньше 100 рублей, то:
- Купи молоко
- Отправься домой
- Скажи купил ли ты молоко
При этом — каждую сложную функцию можно дальше разбить на более подробные и простые
Тривиальные компоненты
Некоторые вещи объяснять дальше не нужно — компилятор знает как это сделать:
- Математические/логические операции — например: сумма, разница, проверка на равенство
- Работа с переменными — определить переменную, запомнить/прочитать значение
- Условия (if) — если выражение истинно, делай одно, иначе другое
- Циклы (while)- исполняй команду пока выражение истинно
- Блок инструкций ({}) — превратить набор последовательных команд в одну большую команду
Может показаться что это весьма скудный набор, но, в Си в конечном счете, всё сводится к нему
Внешние компоненты
Это могут быть весьма сложные действия. Например — открыть файл или напечатать текст в консоль. Но это забота операционной системы и glibc (или аналог). Об этом мы поговорим более подробно в статье [3]. Пока что я дам тебе две «волшебные» строчки:
printf("MyText" "\n"); // Печатает MyText в терминал, MyText можно заменить
printf("%d" "\n", my_var); // Печатает значение переменной my_var в терминал.
// my_var можно заменить
Увы, объяснения того как устроены вещи порой замкнуты друг на друге, поэтому я пока не могу рассказать все подробности того как это работает. Я расскажу. Скоро, но не сейчас.
Комментарии
Если в коде ввести два слеша (//), то весь последующий текст на этой строке будет проигнорирован компилятором. В этой статье там будут объяснения того что происходит. При копировании кода комментарии можно оставить
Кратко о int main()
Код внутри этой функции будет исполнен при запуске твоего приложения. Пока мы будем использовать следующий шаблон:
int printf(const char *, ...); // Объясню в статье [2]
int main(){ // Подробности в [2]
// Тут ты пишешь свой код
// Он будет исполнен при запуске
}
Оставив подробности реализации, перейдем к небольшой практике.
Исполнение шагов
Для начала сделаем что-то тривиальное. Напишем программу что делает несколько шагов:

int printf(const char *, ...);
int main(){
printf("Text1" "\n");
printf("Text2" "\n");
printf("Text3" "\n");
}
Здесь мы последовательно исполнили три команды. Каждая оканчивается точкой с запятой (;). Это не совсем полезно, так что давай попробуем что-то более интересное
Простейшая работа с переменными
Весьма обширная тема, пока ограничимся необходимым минимумом
Так можно определить новую целочисленную переменную:
int имя-переменной = начальное-значение ;
Я буду часто использовать подобную запись. То что выделено курсивом необходимо заменить. Порой надписи объяснят себя, порой будут пояснения
Также стоит учитывать что int
— это ключевое слово типа переменной. В будущем мы встретим и другие типы переменных, но пока нам интересен только int
.
После определения, можно менять значение переменной так:
имя-переменной = новое-значение ;
Здесь важно понимать что это присваивание — при исполнении этой строчки то что слева примет значение того что справа. Это не эквивалент математического знака равно (=)

int printf(const char * , ...);
int main() {
int my_var = 17; // Создание переменной
printf("%d" "\n", my_var); // Её вывод
// Её изменение
my_var = 4;
printf("%d" "\n", my_var); // вывод...
my_var = 8;
printf("%d" "\n", my_var);
}
Выражения
Пожалуй, это одна из самых неприятных тем в этой статье. Остаток гораздо проще. Всё дело в этих записях
int имя-переменной = начальное-значение ;
имя-переменной = новое-значение ;
Дело в том что значения с правой стороны могут иметь не только константы, но и математические, логические операции, переменные, а также вызов функций
Комбинация всего этого — выражение
Пример работы с переменными. Рекомендую внимательно изучить возможные присваивания my_var :

int printf(const char * , ...);
int main() {
// Создание переменной
int my_var = 17;
printf("%d" "\n", my_var); // Её вывод
// Её относительное изменение
my_var = my_var + 1;
printf("%d" "\n", my_var); // вывод...
// Математика
my_var = 3 * 2 + 1;
printf("%d" "\n", my_var); // 7
// Смена порядка счета
my_var = 3 * (2 + 1);
printf("%d" "\n", my_var); // 9
// Логическая проверка
my_var = 10 > 3;
printf("%d" "\n", my_var); // Вывели 1, значит истина
my_var = 10 > 100;
printf("%d" "\n", my_var); // Вывели 0, значит ложь
}
Перед тем как закрыть тему работы с переменными (на пока) приведу таблицу с основными операторами:
Описание | Вид оператора | Порядок исполнения Чем меньше — тем раньше считается |
Логическое НЕ | ! | 2 |
Умножение | * | 3 |
Деление | / | 3 |
Остаток от деления | % | 3 |
Сумма | + | 4 |
Разница | - | 4 |
Различные сравнения чисел (больше, больше-равно, меньше, меньше-равно) | > >= < <= | 6 |
Проверка на равенство | == | 7 |
Проверка на различие | != | 7 |
Логическое И | && | 11 |
Логическое ИЛИ | || | 12 |
Присваивание | = | 14 |
Ветвление при помощи if
Позволяет программе принять одно из двух действий, основываясь на значении выражения:
- Если молоко дешевле 100:
- Купи молоко
- Иначе:
- Устрой истерику
Здесь второе действие (слово иначе) — совершенно дополнительное. Если в случае неудачи ничего делать не нужно, его можно пропустить:
- Если молоко дешевле 100:
- Купи молоко
В программировании всё так-же:
// Первый вариант:
if (milk_price < 100) {
buy_milk();
} else {
panic();
}
// Второй:
if (milk_price < 100) {
buy_milk();
}
Проверяемое выражение пишется в () , код в {}. if и else — ключевые слова что определяют то что тебе нужно
Циклы: 1000-7
Это похоже на обычный if без else, только с тем отличием, что код будет исполняться снова и снова , пока условие не станет ложным, (либо не случится break/return, но об этом в другой статье).
Простейший пример — отнимать от 1000 число 7, пока оно больше 0. Это очень просто реализовать:

int printf(const char * , ...);
int main() {
// Начальное значение
int my_var = 1000;
// Цикл с проверкой того что
// всё ещё нужно считать
while (my_var > 0) {
printf("%d" "\n", my_var); // Печатаем
my_var = my_var - 7; // Отнимаем 7
}
}
Про множественное ветвление
Порой необходимо выбрать из более чем двух вариантов. Для этого мы объединяем несколько обычных if вместе. Допустим, если цена молока меньше 20, мы попытаемся найти причину:
if (milk_price < 100) {
if (milk_price < 20) {
investigate();
} else {
buy_milk();
}
} else {
panic();
}
Это может приводить к весьма уродливым конструкциям. Есть несколько способов борьбы с этим. Один из — использование else if:
if (milk_price < 20) {
investigate();
} else if (milk_price < 100) {
buy_milk();
} else {
panic();
}
Код стал более читаемым, но стоит учитывать, что тут есть зависимость от порядка условий, например:
if (milk_price < 100) {
buy_milk();
} else if (milk_price < 20) {
investigate();
} else {
panic();
}
В данном случае команда investigate никогда не будет выполнена. Если цена молока будет ниже 20, то она будет и меньше 100. Мы его просто купим при первой проверке.
Складываем всё вместе — FizzBuzz
Напоследок напишем весьма популярную базовую задачу программирования — FizzBuzz. Алгоритм такой:
- Для каждого числа от 1 до 100:
- Если число делится на 3 без остатка, вывести Fizz
- Если число делится на 5 без остатка, вывести Buzz
- Но если число делится и на 3 и на 5 одновременно, то должно вывестись FizzBuzz
- Если не делится ни на 3 ни на 5 — вывести само число
Здесь комбинируется всё ранее сказанное

int printf(const char * , ...);
int main() {
int my_var = 1;
while (my_var < 101) {
// Внутри цикла, дальше выбираем
// что делать с числом
if (((my_var % 3) == 0) && ((my_var % 5) == 0)) {
printf("FizzBuzz" "\n");
} else if ((my_var % 3) == 0) {
printf("Fizz" "\n");
} else if ((my_var % 5) == 0) {
printf("Buzz" "\n");
} else {
printf("%d" "\n", my_var);
}
my_var = my_var + 1;
}
}
Это и есть все основы управления кодом
О том что я не сказал
Многое было пропущено для того чтобы не вводить в заблуждение многообразием почти одинаковых, но разных конструкций. Далее я приведу некоторые примеры того что не было рассмотрено
Проклятые инструкции: goto и longjmp
Не используй их, тебя за такое найдут
Это очень специфичные инструкции. Их применение мы рассмотрим в одной из последних статей цикла Си. Пока немного затравки для тех кому интересно — к концу мы увидим самые популярные применения:
- goto — создание С++ «деструкторов» в чистом Си
- longjump — похоже на С++ исключения в чистом Си
- и goto, и longjump — создание дерьмового кода
На этом, оставим эти инструкции…
Альтернативные инструкции for, do while, switch и различные мелочи
Есть множество конструкций что делают примерно то-же самое что и if или while. Их применение иногда может быть более удобным. Пока оставим это всё на потом
Что далее?
Конечно, мы пройдемся по всему что я не сказал, но позже.
В этой статье мы уже обсудили минимальный необходимый набор для создания алгоритма, и лучше сфокусироваться на других важных аспектах. Например, в следующей статье будет рассказано как же работает этот printf, и как создать свои подобные функции