Привет, Хабр! Недавно я задумался: Python — не единственный инструмент, которым я хочу оперировать в своих инструментах. Python, понятно, легко освоить и он применяется везде, но язык-то не идеальный! Ресурсов требует много, да и время выполнения не ахти, а учитывая нынешние темные времена... Мне нужно что-то получше. В общем, тут я вздумал попробовать Си.
Как Си спас инфраструктуру человечества и сделает это еще раз
Стоит отметить, что процесс написания кода на Си пускай и трудоемок, и надо следить за огромным количеством факторов, но этот язык является золотым стандартом производительности, который может переплюнуть разве что ассемблер в руках мастера, чей стаж уходит в десятилетия.
Родился этот язык в 1972 году, когда о таких вещах, как веб-разработка, и речи не шло, а игры тогда были вообще без кода, исключительно аппаратные системы! (Например, игра Pong для компьютера Atari 1972 года)

После своего появления Си стал началом новой эпохи разработки программного обеспечения: пока ассемблер был близок к машинному коду, а Бейсик тогда был неким аналогом сегодняшнего Python, Си знатно задрал лапку под древом разработки инфраструктуры.
Влияние Си на ассемблер:
Ассемблер не был поглощен Си, но потребность в нем заметно снизилась, а его использование резко сократилось. До этого именно на нем писались многие системные программы (и части ОС тоже);
Когда Си предложил все то же самое, но лучше, он начал использоваться разве что там, где Си не справлялся: обработка аппаратных прерываний, работа с привилегированными инструкциями процессора, оптимизация критических мест, и так далее;
Стоит также отметить, что большинство компиляторов Си, такие как GCC или Clang, поддерживают ассемблер, вшитый в код Си. Пример синтаксиса GCC:
asm("movl %eax, %ebx");
Влияние Си на Бейсик:
Бейсик был подвержен влиянию Си куда сильнее. Ранние версии Бейсика (особенно интерпретируемые) подвергались критике из-за ряда проблем: обилие GOTO (спагетти-код передает привет), слабую модульность, ограниченные возможности работы с данными...
Под влиянием Си Бейсик преобразился (стал снова великим). В нем появились процедуры и функции, как в Си, блочная структура кода (BEGIN/END, SUB/FUNCTION), локальные переменные и структуры данных (как struct в Си);
Большинство версий Бейсика начали мигрировать в компилируемый формат (например, QuickBASIC). Код начал оптимизироваться на уровне компилятора, а еще появилась возможность создавать исполняемые программы (.exe-файлы);
Бейсик даже начал потакать Си: в некоторых версиях (FreeBASIC, например) он начал поддерживать интеграцию .h-файлов (заголовочные файлы Си) и компиляцию кода в тот же формат, что и Си.
Время лихое, но для умелых золотое
Перенесемся в современность, в которой даже в той же веб-разработке преимущественно сидят Python (FastAPI) и JS — интерпретируемые ЯП, разработка на которых легкая, но при этом инференс довольно ресурсозатратен.
В данный момент, принимая во внимание суровые реалии, только слепоглухонемая бабка из глубин Сибири не слышала о всемирном кризисе ОЗУ, виной которому наш любимый (нет) нейрослоп, который мы каждый день видим в любом соцсети, даже порой на этом же Хабре.
Использование генеративного ИИ пошло не туда: он проложил красную ковровую дорожку лентяям, которые вооружились Sora и пошли брать штурмом видеохостинги. Нагрузка на мощности огромная, даже пользовательская DDR5 идет нарасхват, а облачные сервисы дорожают.

Да и жесткие диски тоже на низком старте (хранить нейрослоп данные для обучения ИИ, тоже надо). Дал бы руку на отсечение, что дальше в расход пойдет вода (для охлаждения систем)... Но не буду этого делать, от греха подальше.
Понятное дело, что это никуда не годится. Все уже массово оптимизируют свой код. Я еще не знаю, как именно это происходит, но я бы в этой ситуации вынес горячие зоны бэкенда в компилируемые языки. Си — один из них. Пусть я и не уверен, что он вытеснит Go и Rust, но в критических точках он самый производительный.
На нем построено множество современных ЯП (Python, C++, Rust). В сотни раз быстрее Python и поедает во столько же раз меньше ресурсов. Любимец микроконтроллеров, ОС и прочих специфичных разновидностей ПО. Си — спаситель человечества.

Мой личный опыт с Си
Найдя на Степике бесплатный курс по Си и скачав C××droid из Google Play (уровень подготовки — бог), я сразу же побежал писать следующий код:
int main() { printf("Hello, world!") }
...и сразу же с порога получил ошибку. Даже три сразу.
Во-первых, я забыл заголовок #include <stdio.h>. Она содержит самые базовые функции ввода и вывода;
Во-вторых, после каждой строки кода должна быть точка с запятой (";"), мол, "начало мысли" -> "конец мысли". Как в JS, но тут это обязательно;
В-третьих, желательно в функции main() в конце приписывать return 0;. Ошибка более стилистическая для новых компиляторов, но если этого не сделать, то в некоторых случаях вы даже не будете знать, когда программа завершится. Возврат нуля здесь является чем-то вроде выражения "уйти с миром".
После радушных объятий компилятора и анализа своих ошибок мой код выглядел так:
#include <stdio.h> int main() { printf("Hello, world!"); return 0; }
На Python для этого мне бы понадобилась одна строка! Зато вместо миллисекунд Python (конкретно для этого случая) у меня на вывод ушло, ну... Гораздо меньше времени. Вычитая компиляцию в бинарник.
После часа изучения курса по Си я осмелел и даже написал что-то в духе:
#include <stdio.h> int main() { int x = 7; int y = 2; printf("%f", (float)x / (float)y); // вывод: 3.500000 return 0; }
Код объявляет две целочисленных переменных: x (7) и y (2). А затем делит их между собой (до этого переведя их в формат типа 7.0 и 2.0) и получает 3.500000 на выводе.
Небольшое пояснение: в Си при делении целых чисел 7 / 2 получится 3. Деление двух целых чисел работает здесь как оператор // в Python. Если вы хотите получить конкретный результат, то переводите числа в float или double.
Возмужавши от своих подвигов на курсе, я решил слегка выйти за его рамки и написать что-то свое. Например, те же "камень-ножницы-бумагу". Особенно после того, как я узнал, что в stdlib.h есть функция rand(), которая, впрочем, работает чуть иначе, чем в Python. Но об этом осознании чуть попозже.
Веселая нарезка (страданий) моего мозга
Прототип на Python выглядел бы примерно так:
import random plays = ["rock", "paper", "scissors"] # Обработка ввода игрока p_choice = input() while p_choice != "q": # выход при вводе q player = player.lower() # перевод всего ввода в нижний регистр c_choice = random.choice(plays) # выбор компьютера # Логика сравнения if p_choice == c_choice: print("Draw!") elif p_choice == "rock": if c_choice == "scissors": print("You won!") else: print("Computer won!") elif p_choice == "scissors": if c_choice == "paper": print("You won!") else: print("Computer won!") elif p_choice == "paper": if c_choice == "rock": print("You won!") else: print("Computer won!") print() # пропустить строку p_choice = input()
На Си это оказалось гораздо сложнее. Мне пришлось столкнуться с:
указателями строк;
сегфолтами;
определением функций (и я не про int main);
массивами;
условиями;
и прочими буками Си

Начнем с того, что при попытке объявить массив я написал:
char arr[3] = {"rock", "paper", "scissors"};
...НЕТ. То есмь массив из 3 символов, а я пытался запихнуть туда строки. Надо делать либо:
char *arr[3] = {"rock", "paper", "scissors"}; // массив указателей
Либо:
char arr[3][10] = {"rock", "paper", "scissors"}; // двумерный массив из 3 строк по 10 символов // этот способ мне не понравился, так что я взял первый
Потом я принялся реализовывать рандом. Избалованный Python, я попробовал функцию rand() из stdlib.h:
#include <stdio.h> #include <stdlib.h> int main() { char *plays[3] = {"rock", "paper", "scissors"}; int count = sizeof(plays) / sizeof(plays[0]); int index = rand() % count; printf("%s", plays[index]); return 0; }
Вот только чего-то не хватает. Почему-то мне всегда выпала только "бумага": я не задал семя рандома (srand). Для исправления сего недоразумения мне пришлось сделать так:
#include <stdio.h> #include <stdlib.h> #include <time.h> // нужно для time() int main() { srand(time(NULL)); // устанавливаем яблоко рандома char *plays[3] = {"rock", "paper", "scissors"}; int count = sizeof(plays) / sizeof(plays[0]); int index = rand() % count; printf("%s\n", plays[index]); return 0; }
Теперь рандом зависит от того времени, когда программа выполняется!.. И все же можно сделать лучше.
#include <unistd.h> unsigned int seed = time(NULL) ^ getpid(); srand(seed); // устанавливаем яблоко рандома (но лучше!)
Выглядит это так:
мы получаем текущее время на момент запуска;
а также PID операции (все PIDы разные в рамках одной операции ОС);
побитовое XOR (^) комбинирует оба источника энтропии, усиливая перемешку;
Теперь наш генератор работает горвздо лучше!
Позже, когда я добрался до написания условий, я решил посмотреть, как сравнивать строки в Си, и не зря. Тут не прокатит прямое сравнение, как в Python: в Си для этого существует функция strcmp(str1, str2), возвращающая число, равное:
0, если они равны;
-1, если первый ASCII-символ первой строки меньше второго;
1, если наоборот
Прозвучало страшно, но я быстро понял, как это использовать. Я создал условие вида if (strcmp(p_choice, "scissors") == 0), которое определит, ввел ли игрок "scissors". Сам же алгоритм определения выглядит так:
#include <stdlib.h> #include <string.h> int main() { if (strcmp(c_choice, p_choice) == 0) { printf("\nDraw!\n"); } if (strcmp(c_choice, "rock") == 0 && strcmp(p_choice, "paper") == 0 || strcmp(c_choice, "scissors") == 0 && strcmp(p_choice, "rock") == 0 || strcmp(c_choice, "paper") == 0 && strcmp(p_choice, "scissors") == 0) // условия победы игрока { printf("\nPlayer won!\n"); } if (strcmp(c_choice, "paper") == 0 && strcmp(p_choice, "rock") == 0 || strcmp(c_choice, "scissors") == 0 && strcmp(p_choice, "paper") == 0 || strcmp(c_choice, "rock") == 0 && strcmp(p_choice, "scissors") == 0) // условия победы компьютера { printf("\nComputer won!\n"); } }
Не рекомендую вникать в код: из-за своих ограниченных технических способностей в синтаксисе Си я решил не рисковать, за что поплатился качеством человеческого восприятия кода. Если бы эту статью писал ИИ, он бы определенно стал размышлять на тему лишения человечности во имя алгоритмов машин, но я не буду этим заниматься.
После этого я столкнулся с еще одной напастью: почему-то if игнорировал мой ввод! Я не знал, как это произошло (я все же вводил "paper", я искренне не понимаю, как это случилось), но на всякий случай я решил определить функцию, переводящую ввод в нижний регистр.
Видите ли, у Си нет прямого аналога питоновского .lower(). Есть только tolower() из ctype.h, но он охватывает только один символ, а потому мне пришлось объявить первую Си-функцию, которая НЕ является main():
#include <ctype.h> // перевод в нижний регистр void lower(char *str) { for (int i = 0; str[i]; i++) { str[i] = tolower(str[i]); }
Я буду честен: я больше скопировал эту функцию, чем написал ее. Все же я вник, и понял, как она работает:
void— наша функция ничего не возвращает (intпередmain()означает, чтоmain()вернет число);char *str— указатель на строку;forздесь, в принципе, остался в том же амплуа, что и в Python, разве что замаксировался иначе;int i = 0— в тандеме сforиi++образует выражение вида питоновскогоfor i in range;str[i]— как в Python, берет индекс буквы в строке;str[i]=tolower(str[i])— меняет символ на этот же символ нижнего регистра (в Python строки неизменяемые, но в Си вполне)
И в логике ввода:
int main() { printf("Enter your choice:\t"); scanf("%s", p_choice); lower(p_choice); // теперь наш ввод в нижнем регистре printf("Computer picked %s", c_choice); printf("\nYour input: %s", p_choice); }
Неизвестно почему, но опосля определения функции все заработало.
Кстати, о вводе:
char *p_choice = "..."; printf("Введите выбор: "); scanf("%s", &p_choice);
Никогда так не делайте. Тут дорога только в сегфолт (Segmentation fault).
p_choice— это указатель на строковый литерал "...". Они хранятся в памяти для чтения, их нельзя менять;&p_choiceпередавал адрес указателя, а не адрес буфера для строки;Введенная строка летит в память чтения — поздравляем, вы выиграли "Segmentation fault"!
Лично я решил проблему, изменив секцию на оную типа:
char p_choice[100]; // буфер для ввода в 100 символов printf("Введите выбор:\t"); scanf("%s", p_choice); // без &, потому что массив уже адрес
Вконец уставший от сложностей Си, я почти закончил алгоритм. Все, что мне осталось — это сделать цикл while, потому что неудобно запускать программу сначала каждый раз. Но с этим проблем уже не возникло:
while (strcmp("q", p_choice) != 0) { // ...логика игры... // ...тут же и логика ввода пользователя в конце... } if (strcmp("q", p_choice) == 0) //окончание игры { printf("\nQuitting...\n"); }
После тестирования я выдохнул с облегчением. Наконец, я закончил с логикой этой игры, полностью переведя ее с Python на Си. И если в Python мне бы понадобилось 25 строк, то на Си мне понадобилось 45 строк. Большинство из них — фигурные скобки на отдельной строке, да и код кое-где можно было бы оптимизировать, но эта задача пока не для меня.
Моя реализация Си-кода выглядит так:
// #include <stdio.h> в принципе, можно исключить во имя stdlib.h #include <stdlib.h> #include <time.h> #include <ctype.h> #include <unistd.h> #include <string.h> // перевод в нижний регистр void lower(char *str) { for (int i = 0; str[i]; i++) { str[i] = tolower(str[i]); } } int main() { unsigned int seed = time(NULL) ^ getpid(); srand(seed); // устанавливаем яблоко рандома char *plays[3] = {"rock", "scissors", "paper"}; int count = sizeof(plays) / sizeof(plays[0]); int index = rand() % count; char *c_choice = plays[index]; // рандомный выбор char p_choice[100]; while (strcmp("q", p_choice) != 0) { printf("Enter your choice:\t"); scanf("%s", p_choice); lower(p_choice); printf("Computer picked %s", c_choice); printf("\nYour input: %s", p_choice); if (strcmp(c_choice, p_choice) == 0) { printf("\nDraw!\n"); } if (strcmp(c_choice, "rock") == 0 && strcmp(p_choice, "paper") == 0 || strcmp(c_choice, "scissors") == 0 && strcmp(p_choice, "rock") == 0 || strcmp(c_choice, "paper") == 0 && strcmp(p_choice, "scissors") == 0) // условия победы игрока { printf("\nPlayer won!\n"); } if (strcmp(c_choice, "paper") == 0 && strcmp(p_choice, "rock") == 0 || strcmp(c_choice, "scissors") == 0 && strcmp(p_choice, "paper") == 0 || strcmp(c_choice, "rock") == 0 && strcmp(p_choice, "scissors") == 0) // условия победы компьютера { printf("\nComputer won!\n"); } } // окончание игры if (strcmp("q", p_choice) == 0) { printf("\nQuitting...\n"); } return 0; }
Говорите, что хотите, но я горжусь этим кодом. Написал я его на второй день изучения Си (возможно, мне не стоило заскакивать вперед курса и искать решения багов окаяных), но в конечном итоге у меня получилось.

В конце можно замолвить словечко о том, что учить Си пусть и будет нелегко, но зато код будет быстрым и эффективным. Мне понравилось то, что в этом языке все надо настраивать самому, а это уже по-настоящему максимальный контроль. printf() и scanf(), как ни странно, тоже показались мне гораздо дружественнее (никогда такого не было, чтобы я ошибся в указателях с ними. Хоть убейте — не было!). Пусть функции с массивами позже и заставили меня взвыть, но в целом Си мне понравился.
Ожидаю фидбека в комментариях!
Комментарии (70)

Apoheliy
23.02.2026 09:56Возможно, немного не в тему, но:
А какой фидбек вы ожидаете?
То, что вы попробовали Си - это классно. В любом случае это новый взгляд на программирование и управление ресурсами.
То, что в программе есть что доделать - это тоже очевидно:
scanf("%s", p_choice); -> scanf("%99s", p_choice);Результат scanf лучше контролировать, т.к. если вы введёте один пробел, то p_choice рискует остаться неизменным;p_choice на старте лучше обнулить/очистить. Так-то она мусором заполнена, и если это мусор соответствует букве q, то программа сразу же выйдет;т.к. в программе нет отработки "мусорного" ввода (на запрос ввели "123"), то можно сделать немного полегче-получше: пользователь что-то вводит и в цикле по трём строкам идёт сравнение: какой из вариантов он ввёл? Если ни одному не соответствует, то просим повторить. А если вариант найден, то дальше идёт сравнение чисел. ЧИСЕЛ, КАРЛ! Лучше сравнивать числа: 0 тупит 1, 1 разрезает 2, 2 обёртывает 0.
// окончание игрыif (strcmp("q", p_choice) == 0) {printf("\nQuitting...\n");}можно заменить на просто printf ... - в этом месте p_choice и так равен "q".
Прим.: в нативных языках Си, C++ строковый тип считается "не простым" типом данных (в общем, то это так и есть для всех языков, просто это стараются скрыть в тумане). И поэтому от строк стараются "отойти": если можно переключиться на числа - то лучше работать с числами.

Garantia_Tsverga Автор
23.02.2026 09:56Благодарю за критику. Обязательно приму во внимание Ваши советы.

Sap_ru
23.02.2026 09:56Там вообще можно сравнение в два условия запихать если нумерацию перевернуть: 0 побеждает 2, а остальные тупо по увеличению значения..

kmatveev
23.02.2026 09:56В C если тело оператора if состоит из одного оператора, то фигурные скобки можно не писать, на 8 строк меньше.
Логика вашей игры на C и на питоне отличается: в C вы формируете случайный выбор компьютера вне игрового цикла, так что будете угадывать один и тот же вариант.
Плюсанул хотя бы даже за то, что статья написана живым человеком.

Garantia_Tsverga Автор
23.02.2026 09:56Меня уже поставили в известие. Насколько я помню, из-за моего просчета (я не заключил "яблоко рандома" в цикле while) выбор выполняется только один раз, так что выбор тоже будет идти только однажды. Благодарю за комментарий.

kmatveev
23.02.2026 09:56Не совсем. Вызов srand() нужно оставить вне цикла, а вот вызов rand() внести вовнутрь.

AbuMohammed
23.02.2026 09:56Для людей экономящих фигурные скобки после ифа в аду предусмотрен отдельный котел. Имхо).

VM1989
23.02.2026 09:56А вот MISRA C:2012 правило 15.6 рекомендует ставить фигурные скобки во всех случаях. Потому что сегодня там одна строка, завтра две. Да и читаемость улучшается

Garantia_Tsverga Автор
23.02.2026 09:56Вы технически правы, но немного не по адресу. Правила MISRA появились из-за критических багов в коде, которые убивали людей. В основном они идут в промышленной разработке, но в мелких хобби-проектах ими можно пренебречь.

Octagon77
23.02.2026 09:56Ожидаю фидбека в комментариях!
Напрашиваетесь, сударь, напрашиваетесь...
Вы там, в Питоне своём, как сыр в масле катаясь расслабились до наивности детской. Хотите приложение под Андроид - Kivy, хотите фронтенд - да хоть Anvil.works, и так со всем - Питон и ещё совсем чуть-чуть дают результат. А на С - стоит выйти за вот такую ножницы-бумагу... Андроид приложение - извольте, SDK, NDK, Native Activity и куча подробностей, с которыми можно разобраться, но изложены они с точки зрения Котлина, а то и Джавы. Фронтенд - а Вам какой, на Emscripten или Clang/LLVM? Ну и кусок JavaScript в придачу.,
То, что Вы увидели на ножницах с бумагой - это парадный фасад, а на деле может быть так, что пока Вы, пытаясь получить реально полезный результат, будете учить С, а на это неделя по вечерам уже много, и всю обвязку что и есть задница, можно будет выучить весь Rust и получить тот же результат от Cargo, потом выучить Go и получить результат ещё раз от Go (замечаете - и того проще чем с Растом), а остаток времени провести в душевных метаниях - может быть надо было сразу отдаться, ну или примкнуть к, Flutter?
С Flutter, кстати, возможен пример. На Python вы берёте Flet и ездите на Flutter фрирайдером. На C - сначала Dart, потом Flutter, потом Dart FFI и только потом вопрос "а ради чего?". Если кто-то поклялся Вам хорошо платить за C, ну знает или думает что знает ради чего, это одно дело, а если нет...
Так что от меня фидбек такой - бросайте эту ерунду, вы знакомитесь с С так, как это делали тогда, когда альтернатив не было. Вы питонист? Отлично, напишите модуль на С. Убедитесь, что он и на Андроид, с тем же Kivy например, тоже работает. Напишите что-нибудь на SDL или Qt. И только после этого у Вас появится право решать - понравился С или нет.

Garantia_Tsverga Автор
23.02.2026 09:56Разве C не учат перед C++? В моем видении C — только промежуточная ступень, чтобы получше разобраться в тех же C++/Rust. Хотя, по такой же логике, перед C стоило бы посмотреть на Go/CSharp.

tenzink
23.02.2026 09:56Если вам интересен C++ или Rust, то для этого совершенно необязательно учить C. В случае с C++ многое из того, что вы освоите в C будет доступно, но категорически не рекомендовано к использованию. Нет ничего плохого в изучении C, если он интересен сам по себе, но врядли полезно, как подготовку к изучению того же C++

Garantia_Tsverga Автор
23.02.2026 09:56Тогда я просто начал бы писать на C++, как на Python. Тогда геморроя было бы в 10 раз больше. Я предпочту сначала понять, как работают системы с минимумом абстракций, чтобы уже потом плавно перетечь в C++.

tenzink
23.02.2026 09:56Ну, писать на C++ как на python у вас всё равно не получилось бы, как не получилось на C. И не стоит ожидать, что C можно плавно "перетечь" в C++. Зная C, можно легко начать писать работающие программы, которые будут компилироваться C++ компилятором. Но для идиоматичного кода придётся учиться заново, местами переучиваясь от привычек из C

kmatveev
23.02.2026 09:56Можно знать C и не знать ничего больше, его достаточно для многих применений.

Garantia_Tsverga Автор
23.02.2026 09:56Если очень постараться, то можно и API для бэкенда написать на C.
Я сегодня ради эксперимента взял ИИ (я все еще не умею писать на C, могу только вникать в код) и провел бенчмарк FastAPI-обертки + бэкенда на C против уже существующей этой же обертки на TS. Суть программы была в том, что это была админ-панель (пользователи могли регистрироваться, а админы могли их банить).
Я построил архитектуру так, что Python асинхронно сгребает в батчи (10, 50, 100, 200) запросы и посылает их в C-бэкенд. С количеством обрабатываемых батчей росла и скорость обработки (98× скорости при батче в 200 запросов, и оно шло только вверх по метрике!)
На моем динозавре 2009 года с 6 ГБ ОЗУ оно летало. Я завтра попробую уйти от PostgreSQL и посмотреть в сторону RocksDB, там должно быть еще больше. C++ тоже мне интересен, но я пока не буду его трогать, пусть и знаю несколько прикольных фреймворков для обработки данных

Octagon77
23.02.2026 09:56Разве C не учат перед C++?
Наверняка есть и такие случаи, когда учат. Иначе вот такое вот без надобности было бы.

Sic! C и C++ языки очень разные. То, что они местами внешне похожи - следствие того, что на момент создания плюсов поголовно все заинтересованные лица знали C. Далее сходство сохраняли как дань традиции, но сами ответственные лица подчёркивают - это ни в какой степени не являлось целью и делалось строго по возможности.
Изучать C ради лучшего последующего изучения плюсов - ошибка. Чтобы C просто не мешал, его нужно не только основательно изучить, но и к нему привыкнуть, чтобы языки не смешивались один с другим. Если хочется и C и C++, тогда хорошо, если только плюсы - двойная работа в лучшем случае.
Ниже Вы пишете
Тогда я просто начал бы писать на C++, как на Python.
и это очень странно. С одной стороны, как писать как на Пайтон если на плюсах половина кода в одном файле, а другая половина - в другом? С другой стороны, на Пайтон можно писать в любом стиле, в том числе как на плюсах с макросами.
Если языки сами по себе, как хобби или предмет любования логическими, да и алогичными тоже, построениями, не интересуют - никакого смысла в полиглотстве нету. А рассказы типа "я выучил ... и стал лучше ..." - это скорее от нежелания признавать собственные ошибки, чем от искренности.
Например, заинтересовала тема функционального программирования (спецом подальше от C). Тогда можно прочитать про функциональное программирование и подумать как это будет на своём Пайтоне. Где именно прочитать, я не знаю, однако. Если Вы тоже не знаете, ну не удалось выяснить, то берёте книгу по функциональному языку, скажем по Лисп, и читаете по диагонали. Далее то же самое - уложить идеи на Пайтон. Ключевое слово - по диагонали, то есть никак не выучить.
Применительно к C или плюсам - посмотрите на Ассемблер и, если не в курсе то обязательно, на архитектуру машины, особенно на систему прерываний.

Garantia_Tsverga Автор
23.02.2026 09:56А что учить легче — C или плюсы? Не по отделению "по какому языку материалов больше", а скорее по именно усвоению материала

Apoheliy
23.02.2026 09:56На мой скромный взгляд, чистый Си учить намного легче. Вот совсем (и намного) легче. На C++ одни пляски с шаблонами чего стоят!
НО: если рассматривать условно "большие" проекты, то более понятный, чистый, корректный код будет скорее всего на C++ (конечно, если вы на нём умеете). Просто потому, что стандартных средств для типовых задач - их намного больше. И контроль типов лучше. Повторюсь: это всё работает, если вы умеете "в плюсы". Если вы будете на C++ писать как на чистом Си (многие так и делают и это работает) - тогда преимуществ не будет.

Garantia_Tsverga Автор
23.02.2026 09:56Получается, что на C++ в сложности обучения идет кривая до небес, а после этого плато?

Apoheliy
23.02.2026 09:56Попробую дать другую аналогию (скорость + дистанция) (также: сравнение с Питоном сделано для контраста, не нужно сильно придираться):
== Задаём скорость:
Питон - это как передвижение по просёлочной дороге (утоптано, умято, вполне ровно);
Си - это передвижение по перепаханному полю (вроде не проваливаемся по пояс, но если побежим, то можно и ноги повредить);
C++ - это передвижение по утрамбованному полю (двигаемся медленно, но возможность повредить ноги снижается).
== Включаем дистанцию:
Начинающий разработчик - ну что там говорить - это быстро везде, особенно с ИванИванычем;
Продвинутый разработчик:
на Си - добраться до деревни в 5 км на север;
на Питоне - это обойти 10 полян на севере на расстоянии 10 км;
на C++ - это побывать во всех полянах на севере на расстоянии 10 км.
---
А теперь переходим к исходному вопросу: что легче? По-моему, легче прийти в одну известную недалёкую точку, хоть и по перепаханному полю.
Очевидно, у Вас возможны другие приоритеты. Банально, разный темперамент может повлиять: кто-то не готов "медленно, в одном направлении, а деревни всё ещё не видно".
---
Прим.: по-моему, кривая обучаемости здесь несколько неинформативна, т.к. объём навыков (ордината графика) сильно разная - количество полян отличается очень-очень. Соответственно и сравнивать крутизну некорректно.
Прим.Прим.: В C++ нет как таковой сложности обучения до небес (например, если что-то не знаете - то вообще
босса не замочитьничего сделать нельзя): если что-то слишком сложно, то вы такими средствами не пользуетесь (или используете готовые библиотеки) - и всё ок.
Siemargl
23.02.2026 09:56Прим.Прим.: В C++ нет как таковой сложности обучения до небес (например, если что-то не знаете - то вообще
босса не замочитьничего сделать нельзя): если что-то слишком сложно, то вы такими средствами не пользуетесь (или используете готовые библиотеки) - и всё ок.Босс: ты же знаешь С++, у нас на новой платформе проект не собирается, что то с бустом, поправь...

Deosis
23.02.2026 09:56Сам язык С максимально простой, но при этом и за программиста он ничего не делает. Поэтому за всеми подводными камнями нужно следить самостоятельно.
С++ может взять на себя рутину, но надо знать что написать.

Serpentine
23.02.2026 09:56void lower(char *str) { for (int i = 0; str[i]; i++) { str[i] = tolower(str[i]); }А теперь используем:
... char just_char_array[] = {'e', 'n', 'd', 'l', 'e', 's', 's', ' ', 'F', 'u', 'n'}; lower(just_char_array); ...Наслаждаемся UB.

Garantia_Tsverga Автор
23.02.2026 09:56Напомню две вещи:
Этот код я написал на второй день своего изучения C, причем не обошлось без интенсивного поиска информации по абстрактной логике, не говоря уже о багах и сегфолтах;
Код функции предназначен для строк, а не для массивов со строками
Я ценю и уважаю Ваш профессионализм, но надо же вникать в контекст ситуации.

Serpentine
23.02.2026 09:56Я же по этому поводу:
Ожидаю фидбека в комментариях!
Ничего страшного, научитесь еще.
Код функции предназначен для строк, а не для массивов со строками
И про этот "маленький" нюанс узнаете в процессе.

Garantia_Tsverga Автор
23.02.2026 09:56Прозвучало как страшное предзнаменование. Спасибо за предупреждение.

Serpentine
23.02.2026 09:56Указатели, массивы и строки, их взаимосвязь и то, как они передаются (или возвращаются) в функции — очень интересные темы и не такие уж сложные, как может показаться с первого взгляда. Вам должно понравиться.

Garantia_Tsverga Автор
23.02.2026 09:56Это и вправду так.
scanf()дался мне очень легко, да и строки видаprintf("Значение: %d", num)я полюбил даже больше питоновского форматирования строк. Я сам в предвкушении будущей работы с ними.
Ambroyz
23.02.2026 09:56С "дружелюбным" scanf() Вас тоже ждёт сюрприз. Если Вы свою текущую программу на си будете вызывать на беке с передачей туда пользовательского ввода, то дадите remote code execuition.
Дело в том, что scanf не проверяет размер целевого буфера, т.к. физически не может это делать не зная его размер. В итоге он проедется по стеку, делая типичный buffer overflow. В умелых руках этого достаточно для атаки.

IlyasA74
23.02.2026 09:56В его программе все char* заканчиваются нулями (из-за инициализации списком, надо же, сделали такое в C), поэтому UB не будет.
Поправка: scanf формирует null-terminated последовательность char, поэтому в случае
p_choiceUB тоже не будет.

ValeryIvanov
23.02.2026 09:56Хорошо было бы предложить решение, которое бы позволило в данном случае избежать UB. Вопрос только в том, существует ли оно.

oeditus
23.02.2026 09:56Просто решение — разумеется существует, выделите буфер подлиннее, скопируйте туда аргумент, добавьте в конец
\0и работайте с этой безопасной копией. Но так никто не делает, конечно.
ValeryIvanov
23.02.2026 09:56Я просто не понял, что @Serpentine предлагает сделать, чтобы избежать UB непосредственно в функции lower. Какой бы тип не был выбран для строки(указатель, указатель+длина), всё равно пользователь функции сможет передать испорченные данные.

IlyasA74
23.02.2026 09:56Последовательность
charjust_char_array[]должна обязательно заканчиваться нулем '\0', иначе это некорректная строка C.
Sap_ru
23.02.2026 09:56А это не строка. Это массив, вообще-то. Сами-то посмотрите, что у вас объявлено - массив char.

IlyasA74
23.02.2026 09:56Все верно, массив char содержит NUL-terminated строку C, т.е. последовательность символов, которая заканчивается символом NUL (\0).

0x1b6e6
23.02.2026 09:56Ваше утверждение было бы верно, если бы там был строковый литерал, но его там нет.
char arr1[] = "abc"; ///< вот тут есть null в конце char arr2[] = {'a', 'b', 'c'}; ///< а вот тут нет

Asterris
23.02.2026 09:56Омг, ну ты бы ещё на ассемблере пытался это написать - да ещё и изучая язык по ходу.
Я, зная и Си и ассемблер и ещё много других классических языков, точно также буду плеваться от Питона, который не знаю вообще.

oeditus
23.02.2026 09:56В 2026 так на питоне тоже писать не нужно. Объявите пару enum’ов и вперед:
match (player_move, computer_move): # Draw cases case (Move.ROCK, Move.ROCK) | (Move.SCISSORS, Move.SCISSORS) | (Move.PAPER, Move.PAPER): return GameResult.DRAW # Player wins case (Move.ROCK, Move.SCISSORS) | (Move.SCISSORS, Move.PAPER) | (Move.PAPER, Move.ROCK): return GameResult.WIN # Computer wins case (Move.ROCK, Move.PAPER) | (Move.SCISSORS, Move.ROCK) | (Move.PAPER, Move.SCISSORS): return GameResult.LOSE case _: return GameResult.INVALID_INPUT
remindscope
Позабавил тэг "разработка игр"
Автор, вот вы когда сами пишете этот хаос из if'ов, у вас ничего не щелкает в голове?
Garantia_Tsverga Автор
Убрал