Профдеформация и C
C/C++ (именно в таком сочетании) я в базовом варианте изучил за пару летних месяцев перед универом. Тогда для меня он был просто заменой паскалю, и уровень задач был соответствующий — всякая мелочевка для развлечения и разнообразные числодробилки. Универ со своими лабами не сильно что изменил (я получил автомат по программированию в те времена, когда по всем предметам надо было сдавать экзамены и не было балльной системы); Python, JavaScript и даже Java мимо пробегали, но всякие тесты булевой функции на монотонность проще писались на C/C++.
На первых двух работах мне даже платили за то, что я писал на C++, но я тогда был джуном и это все сейчас кажется несерьезным (хотя уже будучи зрелым специалистом написал на C++ модуль ядра для BSD, это был прикольный, но мимолетный опыт). Тем более что потом переключился на Python, оттуда на Scala и понеслось…
В общем, получается, что всерьез, профессионально, на Си я и не писал никогда. А вот в последние несколько месяцев я как раз этим и занимался в рамках научного проекта. И (вполне закономерно) оказалось, что процесс не сильно-то отличается от других языков.
Все стандартные паттерны в наличии, например:
- разделение интерфейса (
.h
) и реализации (.c
) - своего рода полиморфизм можно построить на структурах и указателях на функции
- разделение
api
иimpementation
в зависимостях (где включать заголовок — в.c
или.h
) - всякие билдеры, стратегии и фасады — это вообще легко
typedef
— one love ❤️- даже шаблоны можно сделать, правда макросами
- и т.п.
При этом возникает понимание многих конструкций, которые казались “лишними”: extern
, static
, макросы (увы, некоторые прикольные штуки только ими и получается делать), префиксы для функций из разных модулей (даже в не очень большом проекте словил коллизию имен, неймспейсов не хватает), #ifdef DEBUG
и отдельная сборка под valgrind
или санитайзеры (потому что без отладочного -g
особо и не отладишь утечки памяти, а еще valgrind
не знает всех инструкций из -march=native
и может даже врать про номера строк на -03
). Более того, -Wall
выдает все замечания по делу! Хотя inline
с его приколами все еще невнятный какой-то :/
В более высокоуровневых языках обычно многие вещи делаются гораздо проще (но не во всех конечно *выразительно смотрит на java*), да и “мыслишь” после них более абстрактно. Часто себя ловил на мысли, что вот тут лямбду надо бы, а их особо и нет (только указатели на функции), а вот тут хотелось бы иметь возможность тип менять (вместо void *
), а вот тут частичное применение функции прям зашло бы… Не хватает простых вещей типа Option
, а указатели уже не хочешь использовать, потому что дешевле структуру передать.
Увы, инструменты разработки — не сильная сторона Си. Makefile
еще можно потерпеть, autoconf
сотоварищи — просто жесть, пакетный менеджер — мимо, VS Code опять выбесил по какой-то фигне, а добил меня миллион настроек clang-format
, после которых я “обманываю” форматтер пустым комментарием, чтобы не совсем отвратно выдавал список аргументов функции. Впрочем, ничего нового (осторожно, по ссылке кринж). После космических технологий вроде IntelliJ Idea или Gradle — все очень грустно.
При этом язык все еще развивается. Например, весьма пригодились составные литералы:
return (some_struct_t){
.field1 = value1,
.field2 = .value2, // trailing comma FTW!
}
Сейчас есть стандарт c17
, а еще грядет c23
— и там есть много прикольных штук, про многие из которых я могу сказать: да, такая фича пригодилась бы! Даже лямбды маячат на далеком горизонте, но добавить их — непростая задача.
В общем, писать что-то на низкоуровневом языке достаточно интересно (если это не Zig :)). Это полезное упражнение, чтобы понять, как много делают всякие хорошие инструменты и библиотеки, да и собственный прогресс оценить. “Вернуться к истокам” тоже прикольно. Когда писал пост, откопал в папке со своей универской фигней вот такую хрень, которая датируется 2010 годом:
#include <stdio.h>
class foo
{
friend bool operator < ( bool left, const foo& right );
friend int operator ^ ( int left, const foo& right );
};
bool operator < ( bool left, const foo& right ) { return left; }
int operator ^ ( int left, const foo& right ) { return left; }
int main()
{
int O_o = 0, _ = 0, baka = 0; foo o_O, neko;
bool XD = O_o >_< o_O;
int nya = baka ^_^ neko;
//printf("%d ",nya);
printf("%d ",XD);
return 0;
}
Очевидно, все вышеизложенное хорошо так субъективизировано связанными воспоминаниями из тех времен :) Но даже с учетом этого впечатления останутся положительными.
Что бы там не пророчили, кажется, что Си пока рано умирать.