Очевидный факт: язык C — это основа большого количества современных экосистем программирования. Он обеспечивает фундамент многих операционных систем, базовых библиотек и системных инструментов. При этом все еще не существует единого ресурса, который последовательно и связно отвечает на важные вопросы, возникающие при изучении C: окружающая экосистема разработки языка, выбор инструментов, переносимость кода, управление зависимостями и глубокие аспекты работы.

Своим постом автор Jenny Jam* пытается заполнить этот пробел. Он рассуждает, когда C — идеальный выбор, а когда лучше обратиться к другим языкам. Описывает, как настроить среду разработки и выбрать инструменты, разобраться в версиях, особенностях сборки и тонкостях работы с библиотеками.

Цель статьи — упорядочить представление о языке C и его экосистеме, и, конечно, дать практические советы, которые пригодятся в реальных проектах.

*Обращаем ваше внимание, что позиция автора может не всегда совпадать с мнением МойОфис


Эта статья — ответ на материал Даниэля Бахлера об изучении F# и статью Хиллела Уэйна о том, как трудно изучать новый язык

Этот документ почти наверняка неполный! Информации очень много, и, возможно, я что-то из нее неправильно понял. Я также не знаком со многими экосистемами, такими как Mac, и поэтому не могу рассуждать о них.

  1. Зачем может потребоваться использование С

  2. В каких случаях лучше воспользоваться другим языком

  3. Где можно выполнить код на С

  4. Объединение с C++ (и Objective-C)

  5. Установка среды разработки

  6. Как писать код: текстовые редакторы и среды разработки (IDE)

  7. Где искать помощь и подсказки: документация

  8. Процесс сборки на языке C

  9. Библиотеки

  10. Системы сборки для языка C

  11. Проверка и форматирование кода

  12. Отладка C

  13. Стандарты и версии

  14. Переносимость, неопределенное поведение и безопасность доступа к памяти

  15. Управление зависимостями

Зачем может потребоваться использование C

Язык C во многом является основополагающим языком для других экосистем. Нередко другие языки программирования, частично или полностью, зависят от компилятора C для загрузки собственного компилятора или среды выполнения. Многие языки используют C как де-факто интерфейс внешних функций, а в некоторых системах утилиты представлены в виде библиотек C, к которым вы обращаетесь. Так что если вы, приступая к какой-то задаче, задумаетесь — а не написать ли мне это на C, вполне возможно, что у вас нет других вариантов.

Вот примеры случаев, когда использование С будет уместным:

  • Если вы хотите добавить расширения для нескольких языков. 

  • Когда вам нужно взаимодействовать с системной или пользовательской библиотекой, которая предоставлена только как API на языке C.

  • Когда те, кто использует ваш код в основном используют C FFI.

  • Когда вы пишете что-то, не требующее мощной среды выполнения. Например, ОС или прошивку на системе с ограниченными ресурсами.

  • Когда обращение к более управляемому языку высокого уровня будет слишком медленным или затратным — возможно, в контексте реализации быстрой/эффективной части проекта.

В каких случаях лучше воспользоваться другим языком

  1. Язык C небезопасен с точки зрения доступа к памяти, плюс вам придется самостоятельно управлять ею.

  2. Когда вы работаете с кодом, который должен обезопасить или обработать ненадежные входные данные.

  3. Когда в приоритете не производительность, а скорость разработки или полезность. Например, это актуально при написании сценариев или в исследовательском программировании.

  4. Когда нужно выполнять сложные операции со строками.

  5. Когда требуется большой набор развитых и хорошо проработанных структур данных и алгоритмов.

  6. Когда вы работаете со сложными графами, состоящими из различных объектов с нетривиальным временем жизни, и, следовательно, выигрываете от наличия сборщика мусора.

Где можно выполнить код на C

Язык C почти всегда компилируется (хотя есть и интерпретаторы), поэтому его можно использовать везде, где может выполняться машинный код и есть компилятор. Язык также можно скомпилировать в wasm или javascript с помощью Emscripten — так вы можете выполнять код на C в браузере.

Объединение с C++ (и Objective-C)

Вы наверняка часто слышали, что C и C++ упоминаются в одном ряду или вместе как C/++. Это происходит потому, что, хотя эти языки зачастую совершенно разные, у них близкая история, и C++ включает в себя почти все элементы языка и функции стандартной библиотеки C.

Все основные компиляторы (MSVC, GCC, Clang) воспринимают как C++, так и C (и Objective-C!) — это позволяет использовать код, написанный на C++, из C (или иногда наоборот — это называется Hermetic C++). Несмотря на всё это, у языков разные культуры, нормы и способы выполнения тех или иных действий, и чаще разработчики все же выбирают только С или C++, а не смешивают их.

Установка среды разработки

Набор инструментов, необходимых для создания языка C, обычно называют toolchain. Традиционно он включает в себя компилятор, ассемблер, компоновщик, препроцессор и другие сопутствующие инструменты. Специфика зависит от конкретного toolchain, но в мире *nix они в основном работают по похожей схеме. Обычно вам не нужно беспокоиться об установке той или иной части компилятора, так как все они упакованы вместе.

GCC

GCC (Gnu Compiler Collection) — один из самых распространенных и известных компиляторов, доступных разработчикам. Он бесплатный (вообще, GCC — один из канонических примеров ПО с открытым исходным кодом), и, как правило, может быть установлен с помощью менеджера пакетов вашей системы.

Одно из преимуществ GCC — его возраст: это давно работающий комплексный проект с несколькими бэкендами компилятора и большим количеством дополнительных расширений и утилит. Как правило, если у меня нет особых причин обратиться к другому компилятору, я по умолчанию возьму GCC, хотя Clang не менее достоин и полезен. Но о нем — ниже.

# install on Ubuntu
> sudo apt install gcc

Clang

Ещё один основной toolchain для *nix с открытым исходным кодом. Он был разработан отчасти для того, чтобы унаследовать большинство расширений и настроек компилятора GCC. Это значит, что большинство программ, использующих GCC, могут быть скомпилированы с помощью Clang.

Clang и GCC в основном совместимы по производительности, так что любой из них — отличный выбор, если только вы не ориентируетесь на какой-либо определенный бэкенд или фичу. Также, при создании ПО, полезно попробовать использовать оба, так как каждый из них может найти ошибки, не обнаруженные другим. Одна из особенностей Clang в том, что он (по умолчанию, во всяком случае) является кросс-компилятором со встроенными поддерживаемыми бэкендами, в то время как в случае с GCC вам придется установить сборку компилятора для конкретного бэкенда.

# install on Ubuntu
> sudo apt install clang

MSVC

Это собственный компилятор C и C++ от Microsoft (они относятся к барьеру между языками немного мягче, чем Clang и GCC), и хотя MSVC не является ПО с открытым исходным кодом, он свободно доступен. Чтобы получить его, установите visual studio (обратите внимание, что это не то же, что visual studio code), а версии компилятора до недавнего времени были в комплекте с более широкой средой visual studio, которую можно скачать здесь.

Хотя вам не обязательно использовать интегрированную среду разработки (IDE) Visual Studio, если вы этого не хотите — можете пользоваться автономными инструментами, запускаемыми из командной строки (CLI). Важный момент — это может стать причиной определенных проблем, так как эти инструменты изначально не включаются в среду, а добавляются к ней с помощью специальных ярлыков, которые запускают cmd.exe с каждым из добавленных полезных файлов командной строки. Все подробности есть в этой документации.

Одним из небольших преимуществ CMake является то, что если ваш MSVC установлен в стандартном месте, CMake может определить его местоположение и вызвать инструменты для конфигурирования и сборки.

Mingw

Mingw (и его более современный порт Mingw-64) — это порт GCC для компиляции кода под Windows: либо в виде кросс-компиляции, либо непосредственно в среде Windows. Вообще, именно MSVC — основной способ сборки родного C/++ кода для Windows. Mingw работает и может компилировать много проектов, но у этого toolchain есть недостатки, которых нет у MSVC. Например, использование старой и недокументированной системной библиотеки. Так что если у вас нет веских причин для отказа, я бы рекомендовала использовать MSVC, если вы выполняете компиляцию на родной платформе.

Mingw можно установить в большинстве репозиториев с помощью менеджеров пакетов, а также он загружен здесь.

Как писать код: текстовые редакторы и среды разработки (IDE)

Поскольку языку C уже немало лет, и он используется на многих платформах, у большинства основных текстовых редакторов есть та или иная форма поддержки C/++ — либо встроенная, либо через пользовательские пакеты.

Visual Studio Code

Кроссплатформенный текстовый редактор общего назначения и «облегченная IDE», с огромным репозиторием официальных и поставляемых сообществом плагинов. Этот проект ответственен за создание протокола Language Service Protocol, который был принят в других редакторах. В целом, это удобный и проработанный редактор для многих языков. В нем также есть очень полезный режим удаленного редактирования. Это мой выбор по умолчанию.

Вот инструменты, предоставляемые Microsoft для настройки среды разработки на C/++

Visual Studio

Помимо того что Visual Studio содержит toolchain C/++ и ряд других инструментов, это — полнофункциональная и довольно богатая IDE. У нее есть ряд официально поддерживаемых плагинов для всех основных языков, поддерживаемых Microsoft. Работа по улучшению редактора C для этой среды ведется уже много лет. Обычно я ей не пользуюсь, но это надежная и развитая технология, и если вы разрабатываете код для Windows или в среде Windows, то она отлично для этого подойдет.

CLion

CLion был разработан компанией JetBrains, которая создала ряд редакторов для конкретных языков. CLion — это их инструмент для работы с C/C++. В качестве системы сборки он обычно использует CMake, хотя может импортировать проекты с помощью Makefiles или других инструментов.

Это — платная IDE и она стоит около 100 долларов в год, или меньше, если вы купите полный набор инструментов Intellisense. Мне очень нравится CLion, хотя некоторые опции (например, форматирование по умолчанию) немного неудобны, и мне приходится бороться с ними или редактировать конфигурацию, но это — богатая среда редактирования. К сожалению, экосистема сторонних плагинов в ней гораздо скуднее, чем в других редакторах кода из этого списка.

CLion можно скачать здесь.

Vim

Vim (и его квази-форк Neovim) — это довольно старая и весьма продвинутая среда редактирования на основе командной строки. По умолчанию в ней есть только подсветка синтаксиса для кода, но она обладает огромной базой кода для разработчиков, так что вы можете совершенно точно наполнить ее множеством полезных вещей. В общем, одно из главных преимуществ среды Vim в том, что она, скорее всего, есть на большинстве компьютеров, и вы можете использовать ее, если кодите удаленно по протоколу SSH.

Где искать помощь и подсказки: документация

Справочные страницы

Исторически, источниками информации по функциональности языка C являются справочные страницы (man-pages): база данных документации и клиент в режиме командной строки, который предустановлен почти на всех компьютерах с операционной системой *nix. Как правило, в справочные страницы помещают свою документацию утилиты, системные и другие библиотеки. Обычно она пишется в Roff — простой системе набора текста.

Справочные страницы упорядочены по разделам, которые выступают в качестве пространств имен (подробнее см. на этой man-странице). Как правило, нужные вам блоки находятся в разделе 1 (инструменты командной строки), разделе 2 (системные вызовы и их функции-обертки), разделе 3 (библиотечные функции) и иногда в разделе 7 (обзор или общие темы).

Онлайн-версии руководств размещены в нескольких местах (одно из них — проект ядра linux), но они могут отличаться от версий, установленных на вашем компьютере. Вместо этого я рекомендую очень полезный локальный веб-сервер DWWW. Он преобразует справочные страницы в HTML и индексирует их для поиска.

К сожалению, справочные страницы вышли из моды, поэтому проекты почти перестали их предоставлять, или они довольно скудны.

Gnu Info

Gnu Info была разработана как своего рода модернизация man-страниц. Последние были небольшими и автономными, в то время как Gnu Info должна была представлять из себя набор связанных страниц в виде гипертекста.

Gnu Info используется не слишком часто (по моему опыту, даже не так часто, как man-страницы). К ней прибегают во многих проектах Gnu (зачастую в них предоставляется очень скудная man-страница, сообщающая, что вы должны искать информацию в Info), но я думаю, что это в основном пройденный этап. Даже проекты Gnu сейчас все чаще предоставляют документацию в HTML или PDF – обычно я к ним и обращаюсь.

Локально создаваемая документация: Doxygen

Doxygen – это и инструмент, и общий формат для предоставления документации внутри файла C++. Хотя нередко разработчики используют комментарии в стиле doxygen для добавления документации в код, но не используют собственно Doxygen, обращение к нему довольно характерно для старых проектов.

Если у проекта есть документация doxygen, вы можете собрать его локально, и у вас будет актуальная версия. Действующие проекты часто размещают свою документацию в сети, но обычно только для последнего или долгосрочного релиза.

Microsoft Development Network — MSDN

Microsoft предоставляет документы по своим компиляторам, языковым расширениям и библиотекам на MSDN. Эта система, подобно man-страницам для Linux, часто выступает в качестве основной формы документации.

Процесс сборки на языке C

Если вы переходите с других языков, то компиляция в C для вас выглядит несколько необычно: как правило, вы компилируете каждый файл *.c в объектный файл *.o; затем соединяете их вместе в конечный исполняемый файл с помощью компоновщика.

Если у вас есть проект с файлами:

  • foo.h

  • foo.c

  • main.c

  • util.h

то компиляция с традиционным инструментарием в стиле unix будет выглядеть так:

> cc -C foo.c -o foo.o
> cc -C main.c -o main.o
> ld main.o foo.o -o main

Хотя я и назвал эту модель «необычной», единственная ее странность — включение в текст заголовочных файлов. Всё остальное в принципе похоже на то, что делает большинство компилируемых языков. Традиционная среда разработки C всего лишь делает это более явным образом.

Одно из преимуществ всего этого — частичная сборка: если вы сохраняете объектные файлы после компиляции чего-либо, вам нужно перекомпилировать объектные файлы только для тех C-файлов, которые вы обновляете.

В зависимости от вашей системы сборки промежуточные результаты компиляции могут храниться либо в тех же каталогах, что и исходный код (обычно это называется сборкой в дереве), либо в отдельном каталоге сборки (сборка вне дерева). Проекты, основанные на Makefile, в значительной степени склоняются к сборкам в дереве, а основанные на CMake — к сборкам вне дерева.

Включения и пути включения

В языке C есть два способа включения заголовочных файлов:

#include <stdint.h>
#include "somefile.h"

Технически определение этих способов включения зависит от реализации. Но универсально смысл, приписываемый им, заключается в том, что в случае <> поиск включаемого файла происходит по некоторому глобальному набору путей, в то время как стиль включения "" предполагает, что поиск вначале выполняется в локальных каталогах.

В обоих случаях есть набор путей, заранее определенных вашей средой разработки и инструментарием, и вы можете добавить файлы в путь включения в качестве аргумента командной строки: обычно -I /include/path. На это рассчитаны те проекты, в которых заголовочные файлы перенесены в отдельный каталог, хотя обычно эта задача в различных версиях решается системой сборки.

Макросы и препроцессор

Препроцессор C также с технической точки зрения является автономным инструментом, который можно запускать отдельно для файлов, написанных не на языке C. Хотя единственный другой достойный упоминания пример, который я могу вспомнить, — это Fortran. Он все же довольно сильно связан с определением языка, и макросы C должны следовать правилам допустимых идентификаторов в коде C (хотя, поскольку это текстовые макросы, вы можете делать с ними всякие дикости, например, передавать + в качестве аргумента функции).

Если вам когда-нибудь понадобится отладить макрокод, вы можете вызвать препроцессор командой gcc -E code.c -o code_preprocessed.c. В среде *nix MSVC использует отдельные аргументы.

Код ассемблера и программа-ассемблер

Технически, в конвейере преобразования кода C в объектный файл есть второй промежуточный этап, на котором вы компилируете код в формат, традиционно называемый кодом ассемблера. Этот код (в некотором смысле) является текстовым представлением того, как выглядит машинный код для вашей целевой архитектуры, и он преобразуется в машинный код с помощью программы-ассемблера. Обычно этот формат не выводится, если вы специально об этом не попросите На машинах *nix он традиционно имеет расширение файла .s или .S (если вы используете препроцессор), а на Windows — .asm.

Помимо того, что это может пригодится для просмотра текстового вывода конечного кода, вы также можете писать файлы на языке ассемблера и вручную вызывать ассемблер, например, foo.s -o foo.o. Обычно это не нужно, но иногда применяется, если вы хотите получить экстремальную производительность и необходимо вручную настроить вывод кода, или для реализации низкоуровневых системных вызовов или функций libc, которые нельзя сделать на C.

Обратите внимание, что синтаксис кода ассемблера сильно зависит от конкретной задачи и производителя. У каждой архитектуры компьютера есть свой собственный синтаксис и особенности, а семейство архитектур X86 позаботилось о том, чтобы сделать все возможное, имея два синтаксиса. И даже два конкурирующих ассемблера могут отличаться в деталях, выходящих за рамки базового синтаксиса.

Компоновка и компоновщик

Последним этапом традиционной сборки является компоновка (linkage), где вы берете весь промежуточный код в объектных файлах и объединяете их в конечный исполняемый файл или библиотеку. Этот этап включает в себя компоновку секций данных в исполняемом файле, а также проверку правильности компоновки вызовов функций, определенных вне объектного файла.

Существует собственный язык для управления и манипулирования поведением компоновщика —  скрипт компоновщика. Вам вряд ли придется иметь дело с ним дело, если только вы не пишете компилятор. У МакЯнга есть отличное руководство по подробностям взаимодействия со скриптом компоновщика.

Объединение в библиотеки

В среде *nix вы обычно собираете статическую библиотеку, используя ar (первоначально — менеджер архивов), чтобы собрать все файлы в пакет. Общие (совместно используемые) библиотеки собираются с помощью специального флага в компиляторе. В то время как эти средства создают файл для дальнейшего использования инструментарием (а для динамических файлов — загрузчиком), обычно требуется более сложный набор шагов для фактической упаковки кода для распространения его среди других библиотек.

Библиотеки

Если вам нужно использовать функциональные возможности, которые не встроены в язык, вам придется прибегнуть к помощи библиотеки.

Библиотеки — это способ связать воедино функции для выполнения действий, а также для взаимодействия с операционными системами. Например, SDL— это библиотека с функциями, позволяющими рисовать на экране; libjson— это библиотека с функциями для разбора и сериализации данных в формат обмена JSON, и так далее.

Есть две функциональные разновидности библиотек: статические и динамические; и та и другая используют заголовочные файлы для определения своих интерфейсов.

Заголовочные файлы

Технически они не считаются отдельной конструкцией в стандарте C, но де-факто встроены в соглашение об именовании файлов: файлы, предназначенные для #include, имеют расширение .h.

Заголовочные файлы часто используются внутри проектов для логического разделения их деталей. Часто бывает так, что для одной части программы существует пара файлов .c и .h, например, parser.h и parser.c для кода компилятора для разбора кода.

Заголовочные файлы также используются для описания интерфейса, который библиотека предоставляет своим пользователям. Обычно заголовочные файлы содержат прототипы функций, реализованных в библиотеке, определения типов, с которыми они работают, а также макросы как для утилиты, так и для описания функциональных возможностей, специфичных для архитектуры. Единственный способ, которым библиотека может предоставить доступ к своим функциям и конструкциям, — это через набор заголовочных файлов.

Статические библиотеки

Технически это более старая из двух концепций. Статическая библиотека — это объединение функций таким образом, чтобы их можно было подключить к двоичному файлу в процессе его создания. Это позволяет встроить функционал в двоичный файл и не зависеть от наличия динамической библиотеки в системе пользователя.

Некоторые из преимуществ статического связывания заключаются в том, что вы можете задать функционал во время компиляции, а также отсечь все функции и значения, которые не используются в двоичном файле.

В некоторых крупных проектах также используются статические библиотеки для разделения реализации и использования: если вы создаете видеоигру, то можете поместить всю основную логику в библиотеку libgame в функцию startgame(), и получить один исполняемый файл под названием game, который сразу вызывает startgame(), и другой исполняемый файл, который, в отличие от первого, является просмотрщиком моделей или звукового текста.

Недостатки статической компоновки заключаются в том, что вы постоянно фиксируете информацию в теле программы, а не полагаетесь на потенциально подверженные обновлению динамические библиотеки. Это может стать серьёзной проблемой, если в версии вашей сетевой библиотеки есть ошибка, которая приводит к уязвимости в безопасности, или если при изменении Unicode добавляется ряд новых диграфов, с которыми старая версия библиотеки не может справиться.

Динамические библиотеки

Динамические библиотеки противоположны статическим: функции, которые вы используете, не загружаются до запуска исполняемого файла, а затем эти функции подтягиваются, так что вы всегда используете ту реализацию, которую предоставляет система, даже если вы замените эти функции на другие.

Также динамические библиотеки часто используются для плагинов: поскольку вы можете вручную загружать библиотеки и связанные с ними значения с помощью Unix dlopen()/dlsym() и Windows LoadLibrary()/GetProcAddress(), вы можете загружать дополнительную логику для выполнения, не вставляя ее внутрь исполняемого файла. В видеоигре это можно сделать для загрузки модов, а в текстовом редакторе – для загрузки плагина с поддержкой нового языка программирования.

Преимущество динамических библиотек в том, что они всегда загружаются обновленными, а значит, вы можете исправить ошибки безопасности или добавить новый функционал в старые программы. Кроме того, они позволяют экономить системную оперативную память – если вам не нужно загружать копию математических функций в каждый запущенный двоичный файл, то вы экономите много дополнительного места.

Проблема с динамическими библиотеками заключается в следующем. Для использования указанных преимуществ необходимо убедиться в том, что у библиотеки правильная версия, соответствующая той, что собирается использовать ваш исполняемый файл. В худшем случае это подразумевает поставку копий динамической библиотеки, на которую вы опираетесь, вместе с вашим кодом.

Libc и другие специальные библиотеки

Есть одна библиотека, которую обычно выделяют для особых целей: стандартная библиотека – обычно ее называют libc. Почти все программы, работающие в среде операционной системы, используют libc. Она открывает большинство стандартных функций, а также манипулирует некоторыми основными функциями среды выполнения, такими как обработка файлов и сигналов. Хотя libc может быть просто специальной библиотекой, она часто тесно связана с системой среды выполнения, и некоторые компиляторы жестко задают ожидания относительно ее содержимого.

Помимо libc, есть еще несколько библиотек, которые находятся в особом месте, где они, как правило, связаны с компилятором (или даже являются частью libc):

  • libm, которая реализует несколько математических функций;

  • libdl, реализующий функции для работы с динамическими библиотеками.

Библиотеки только с заголовками

Эти библиотеки больше распространены в мире C++, но, похоже, они находят применение и в C. Из-за сложности систем сборки и разнообразия подходов разработчики иногда используют возможность объявления функций с ключевым словом `inline`, чтобы включить как прототипы, так и их реализации в заголовочный файл. Это позволяет, после загрузки библиотеки на систему, избежать дополнительной сборки — достаточно просто подключить её с помощью `#include <headeronly.h>`.

Системы сборки для языка C

Как и другие более старые языки программирования, C появился до того, как возникла тенденция включать в компиляторы языков расширенный набор инструментов и рекомендованную структуру проекта. Вместо этого для работы с C существует довольно широкий выбор различных инструментов, и каждый из них неявно предлагает свою структуру проекта.

Зачем использовать систему сборки

Вы можете достаточно просто написать сценарий сборки в виде gcc *.c -o foo, но хотя такой способ может сработать для маленьких проектов, он быстро превратится в проблему, если ваш проект очень крупный или у него более сложная компоновка.

Системы сборки — это способ описания действий, которые необходимо предпринять для компиляции набора исходных файлов в конечную форму (или несколько конечных форм). Они позволяют описать это в более краткой, декларативной форме: работа с версией командной строки как таковой может оказаться запутанной, если вам нужно скомпилировать foo_windows.c только под Windows, а нужно скомпилировать foo.c и под Linux, и под MacOS.

Кроме того, системы сборки отслеживают, когда вы в последний раз редактировали файл, и компилируют только те файлы исходного кода, которые были изменены. 

Это может не выглядеть серьезной проблемой, если вы работаете с небольшим проектом, который компилируется менее чем за секунду, но становится критически важным, когда речь идет о масштабной кодовой базе, компиляция которой занимает часы.

Make (GMake, BMake)

Make — это, пожалуй, самая старая система сборки. В то время как POSIX определяет очень ограниченное подмножество правил и утилит, у основных реализаций Make (GNU Make и BSD Make) есть гораздо больший набор возможностей.

Как правило, я использую Make для небольших проектов и простых примеров, потому что его очень легко раскрутить и постепенно добавлять в него элементы. Обычно я избегаю его, если предполагается, что проект будет довольно большим или в нем используется сложная условная логика.

Пример для нашего небольшого проекта может выглядеть так:

.PHONY: all clean

CFLAGS= -Wall -Wextra

all: foo

foo: main.o foo.o 
    @$(CC) $(CFLAGS) main.o foo.o $(LDFLAGS) -o foo

main.o: main.c foo.h
foo.o: foo.c foo.h

clean:
    @rm -rf *.o foo

Ninja

Ninja похож на Make с большими ограничениями, но он был разработан для систем мета-сборки, таких как описываемый ниже CMake. Он был создан, чтобы обеспечить быстроту выполнения и сборки, и чтобы ничего не нужно было писать вручную.

CMake

CMake позиционирует себя как "система мета-сборки" – вы пишете сценарии сборки проекта на языке сценариев CMake, а затем они компилируются в целевую сборку, подходящую для данной системы (например, для Makefiles, Ninja, Visual Studio Code). Он также выступает в качестве своего рода системы конфигурации, чтобы библиотеки, использующие CMake, могли быть легко собраны и использованы другими проектами, также применяющими его, с минимальным количеством связующего кода.

Также у CMake есть тенденция изменять структуру проектов: в то время как проекты в стиле Make обычно помещают заголовочные файлы в то же расположение, что и файлы реализации, проекты CMake могут помещать их в отдельное место и затем добавлять в путь включения заголовков с помощью команд. Это означает, что у вашей IDE, вероятно, должно быть некое средство рассмотрения команды CMakeLists.txt или ее вывода, чтобы позволить intellisense правильно обрабатывать код.

Обычно я пользуюсь CMake, если ожидаемый проект – большой и со сложной логикой, или если я использую C++. 

У него довольно плохая репутация, но, если вы усвоите хороший способ структурирования, я считаю, что он не так уж и плох — просто многословный. Я предпочитаю использовать большинство рекомендаций по стилю из «Введения в современный CMake».

Система сборки Visual Studio

Visual Studio C имеет собственный формат (который представляет собой специализированный XML-файл) и свой инструмент сборки; но на самом деле предпочтительно редактировать его через открытый интерфейс GUI. Я достаточно часто его использовал, но в нем довольно сложно отображать графические интерфейсы в текстовом виде. Я ничего не имею против этого инструмента: если вы делаете что-то под Windows и используете Visual Studio, вам, вероятно, следует пользоваться либо им, либо CMake.

Проверка и форматирование кода

Полезно, особенно при программировании на языке C, иметь инструменты, которые ищут ошибки и другие проблемы в статическом коде, а также используют отладку во время выполнения. Они могут пригодиться для автоматической CI/CD-проверки запросов на включение изменений или кода, добавляемого в основную ветвь проекта.

Предупреждения компилятора

Первая линия защиты — это предупреждения компилятора. По умолчанию компилятор C позволяет многим потенциальным ошибкам, которые он может обнаружить статически, пройти незамеченными, поэтому считается лучшей практикой включать эти предупреждения. Обычно компиляторы предоставляют флаги как для отдельных предупреждений, так и для их групп, которые можно активировать. В GCC и Clang это обычно выглядит как -Wall или -Wall -Wextra, а в MSVC — как /W4.

Кроме того, IDE может воспользоваться отмеченными ошибками, чтобы показать вам потенциальные проблемы.

Обратите внимание, что даже использование этих групп может не включить все возможные предупреждения.

Предупреждения компилятора как ошибки

В компиляторах также присутствует опция, позволяющая превратить любое обнаруженное предупреждение компилятора в ошибку. В MSVC это /WX, а в GCC и Clang — -Werror. Это, как правило, считается лучшей практикой, но вызывает определенные споры. Некоторые из более продвинутых предупреждений в компиляторах могут действовать скорее как правила стиля или сигнализировать о ситуациях, которые в большинстве случаев не являются ошибками. Это может привести к так называемой «усталости от сигналов тревоги».

По крайней мере, в GCC можно вручную указать, какие именно предупреждения следует рассматривать как ошибки, что, вероятно, лучший выбор при добавлении дополнительной строгости кода в существующую кодовую базу, где функция «предупреждения как ошибки» ещё не была включена.

Если вы разрабатываете код «с нуля», стоит включить эту функцию.

Clang Format

Это хорошо настраиваемый автоматический форматировщик кода в стиле gofmt, но в духе гетерогенной среды языка C. У него есть несколько встроенных предустановок, которые можно изменять. Я никогда не использовал Clang Format, но большинство проектов с открытым исходным кодом, которые я просматривал, имеют конфигурационный файл .clang-format, расположенный где-то на верхнем уровне кодовой базы. Как и многие другие инструменты, его полезнее внедрить в процессе разработки «с нуля», чем добавлять позднее.

Инструменты статического анализа и линтеры

Хотя компилятор может быть основным средством статического анализа кода, поскольку у него есть довольно глубокое представление о кодовой базе во время компиляции, сейчас есть более сложные и продвинутые инструменты для проверки и верификации кода на языке C. Однако похоже, что к этим инструментам прибегают довольно редко: я использовал CppCheck дважды, и я не слышал о проектах с открытым исходным кодом, в которых они используются, но я могу ошибаться. Кроме того, похоже, что до недавнего времени они в основном входили в сферу коммерческого ПО и выходили за рамки бюджетов индивидуальных разработчиков.

Основные из известных мне бесплатных — clang-tidy и cppcheck, оба с открытым исходным кодом и легко доступны.

Отладка C

Отладка C — это то, о чем многие новички поначалу даже не задумываются. Однако по мере роста кодовой базы появляется необходимость проверять программу в процессе ее выполнения и находить возможные ошибки. Освоение методов и инструментов отладки абсолютно необходимо для долгосрочного обслуживания ПО.

printf()

Это может показаться глупым, но иногда полезно распечатать информацию, чтобы иметь представление о том, что происходит в вашей системе. В более продвинутых проектах разрабатывается библиотека протоколирования, которая представляет собой просто более сложный printf, позволяющий добавлять дополнительную информацию о том, где был сделан вызов, и контролировать глубину выводимой информации.

Отладчики (gdb, lldb, windbg)

Если printf() не справляется с задачей, или вы получаете сообщение об аварийном завершении (SEGFAULT), то вам нужно обратиться к отладчикам. Они используют преимущества системных перехватчиков (hooks), позволяя вам выполнять программу построчно и проверять значения переменных и параметров, а также показывать, когда происходят SEGFAULT'ы или приходят другие сигналы.

GDB — один из самых популярных. Он предоставляет как интерфейс командной строки, так и интерфейс удаленного протокола, чтобы вы могли отлаживать код на удаленных машинах.

Windbg — это специфический для Windows отладчик, который имеет два фронтенда и может также использоваться для отладки информации на уровне ядра.

Чтобы использовать отладчики, вам, вероятно, придется перевести код в режим отладки/низкой оптимизации и добавить отладочные символы, которые нужны отладчикам для интроспекции двоичного файла.

Valgrind

Valgrind — это гораздо более тяжеловесный отладчик, эффективно работающий как интерпретатор машинного кода. Вы загружаете в него двоичную программу, и он позволяет обнаруживать ошибки, которые не очевидны до момента выполнения, включая более тонкие проблемы, такие как некорректное использование файловых дескрипторов.

У него значительные накладные расходы на скорость, что может затруднить его использование в некоторых проектах, особенно если они потребляют много памяти. Однако он предоставляет широкий набор инструментов для обнаружения ошибок.

Санитайзеры

Они выполняют схожие с Valgrind задачи (поиск ошибок более высокого уровня во время выполнения), но в отличие от последнего не требуют загрузки двоичного файла в интерпретатор; вместо этого все проверки и инструменты времени выполнения устанавливаются в двоичный файл во время компиляции. Это позволяет вам просто запускать двоичный файл как обычно и видеть ошибки только в случае их возникновения.

К сожалению, санитайзеры частично несовместимы друг с другом, поэтому комбинации санитайзеров, которые можно включить одновременно, ограничены. В частности, UBSan и ASan несовместимы друг с другом.

Стандарты и версии

C — стандартизированный язык, что означает наличие набора канонических документов, описывающих его, а также среду выполнения, окружение и другие аспекты. Эта тенденция стала менее популярной в новых языках, поскольку разработка компиляторов теперь более открыта. Однако исторически стандартизация была ценным инструментом, позволяющим избежать случайного использования специфичных для вендоров расширений, которые могли бы затруднить переносимость вашего кода на другие платформы.

Стандарты

Основные версии языка C изложены в документах стандартов ISO. К сожалению, доступ к самим документам по стандартам возможен за существенную плату, поэтому большая часть сообщества полагается на использование финальных черновиков, поскольку они почти наверняка очень близки к окончательному стандарту.

Это не просто гипотезы: и GCC, и Clang используют опцию, определяющую, какой из версий стандарта вы хотите придерживаться. Компании или отдельные проекты часто строго указывают, какой стандарт разрешен, а разные стандарты открывают специфические функциональные возможности.

Разработчики, заинтересованные в переносимости, часто ориентируются на старые версии стандарта, обычно либо C89, либо все чаще C99. Если вы используете аргументы командной строки для выбора конкретного диалекта, вы можете отключить доступ к некоторым функциям, определенным POSIX или вашим локальным окружением.

Предварительная стандартизация C

C существовал как язык по крайней мере за 18 лет до того, как был создан комитет по разработке стандартов C, и за это время почти каждый производитель разработал свой компилятор с собственными возможностями и расширениями синтаксиса. Наиболее близким к стандарту из существующих был учебник, написанный некоторыми из первых разработчиков C, — «Язык программирования C», и сведения о языке, представленные в первом издании, иногда называемом K&R C.

C89 (он же ANSI C)

Проект можно посмотреть здесь.

Это самая ранняя стандартизированная версия C. Часто именно на нее ориентируются люди, желающие сделать свой код пригодным для использования на различных платформах. Основное отличие этой версии от более поздних в том, что все переменные должны быть объявлены в начале блока, а в библиотеку позже было добавлено несколько стандартных типов и функций.

Забавный факт: эталонный компилятор C89 на самом деле был написан на предварительно стандартизированном C++.

C99

Проект можно посмотреть здесь

C99 — это другая основная версия языка C, которую многие рассматривают как в целом безопасный общий знаменатель. Основные синтаксические дополнения, добавленные в него: структуры переменной длины (реализуемые посредством элементов — динамических массивов) и массивы переменной длины (позже сделаны необязательными в C11), типы комплексных чисел, стиль однострочных комментариев C++ //, объявления переменных, разрешенные в середине блока, и добавление нескольких типов для арифметики целых чисел фиксированной длины.

C11

Проект можно посмотреть здесь

В C11 некоторые из новых добавлений C99 (например, массивы переменной длины) стали необязательными. В нем также добавлена ограниченная форма обобщённых типов, обрабатываемых во время компиляции, с помощью директивы _Generic, а также поддержка потоков и соответствующих многопоточных примитивов.

C17

Проект можно посмотреть здесь

C17 был довольно небольшим обновлением стандарта, в котором добавлено несколько вещей и в основном отменены или прояснены позиции проекта C11.

C2X

В текущей версии стандарта, над которой ведется работа, произошли значительные изменения, поэтому, вероятно, этот раздел скоро устареет. В нем изменен ряд технических деталей (несколько старых макросов теперь стали ключевыми словами), введены атрибуты в стиле C++, введена директива препроцессора, используемая для встраивания файлов непосредственно в виде двоичных массивов.

POSIX

POSIX ссылается на набор спецификаций, созданных в 90-х годах и впоследствии обновленных, в попытке найти минимальный набор интерфейсов, инструментов и функциональных возможностей библиотек, которые были бы общими между ними. Библиотеки, определенные в POSIX, — это «оставшиеся», и их краткий список можно найти здесь.

Расширения Glibc и GCC

GCC и основная реализация libc проекта GNU (glibc) предоставляют огромное количество функций расширения и утилит, и программное обеспечение стало опираться на них. Как и в Windows, разработчики glibc сосредоточились на обратной совместимости, поэтому он выступает в качестве некоего стандарта де-факто, как и среда Win32.

Среда Win32

Хотя Windows не определяет никаких стандартов, описывающих ее библиотеки и интерфейсы, Microsoft уделяет большое внимание обратной совместимости. Это означает, что вы можете эффективно полагаться на среду Win32 C как на стабильную среду для создания программ.

Переносимость, неопределенное поведение и безопасность доступа к памяти

Неопределенное поведение — это еще одна из основных странностей языка C, с которой, вероятно, сталкивается каждый разработчик.

Иерархия поведения

В стандартах языка C существует своего рода иерархия соответствия кода стандарту. Как и многое в стандарте C, сочетание официального языка с прозаическим описанием операционной семантики языка поначалу озадачивает, если вы с ним не знакомы.

  • Определенное поведение: код, поведение которого реализация языка C должна обрабатывать, и результат которого, как ожидается, будет одинаковым для всех компиляторов.

  • Поведение, определяемое реализацией: код, который должна обрабатывать допустимая реализация языка C, но результат которого может быть задан самой реализацией.

  • Неопределенное поведение: код, который соответствующая реализация языка C не обязана обрабатывать, и стандарт не предъявляет никаких требований к тому, что может произойти — реализация вообще не ограничена в том, что делать, и может по сути делать что угодно.

Однако, по крайней мере, в кругах пользователей компиляторов, термин «неопределенное поведение» также используется для описания любого кода, который не характеризуется определенным поведением для конкретной реализации. В таком случае, я думаю, лучше обозначить это как «нарушения инвариантов компилятора», которые могут вызвать ошибки компилятора, или привести к тому, что компилятор выдаст код с ошибкой, или к тому, что в большинстве случаев код будет работать, но в нескольких случаях – нет.

Абстрактная машина C

При изучении стандарта C большинство описаний языка опирается на концепцию "абстрактной машины" — своего рода идеализированного интерпретатора для C, который преобразует и выполняет код без множества специфических деталей.

Непреднамеренная непереносимость

Очень легко непреднамеренно положиться в вашей среде разработки на специфическое или даже неопределенное поведение, которое работает так, как вы думаете, а затем обнаружить, что код полагается на эти функции таким образом, что это приводит к ошибкам, если вы попытаетесь перенести его на другую платформу.

Это не просто гипотеза или что-то, возникающее при работе с непонятными и экзотическими процессорами. Это, например, происходит с людьми, переносящими игру с одной из самых популярных игровых платформ (PC) на другую очень популярную игровую платформу (Nintendo Switch):

Теперь, когда с этим мы разобрались, пришла очередь многопользовательского детерминизма. Одной из главных целей было не вырезать мультиплеер из игры. Более того, я хотел, чтобы игроки на ПК могли играть с игроками на Nintendo Switch. Нам впервые нужно было убедиться, что игра детерминирована между ARM и x86.

Мы подумали, что всё будет в порядке — C++ ведь переносимый язык, правда? Главное — не использовать неопределённое поведение. Однако выяснилось, что мы используем довольно много неопределённого поведения, как в основном коде, так и в библиотеках. Например, при приведении double к целому числу, если значение не помещается в целое число, это считается неопределенным поведением, и полученные значения будут различаться на процессорах ARM и x86.

https://factorio.com/blog/post/fff-370

Не ждите бесплатной переносимости

Возможно, вам не стоит полагаться на неопределенное поведение, которое работает в вашей производственной среде, потому что это может привести к ошибкам в дальнейшем или к тому, что код не сможет компилироваться, если вы обновите версию компилятора. Но стоит задуматься, хотите ли вы взять на себя труд и хлопоты по созданию кода, который может и должен компилироваться даже на самых экзотических платформах, потому что, несмотря на то, что C — переносимый язык, очень легко написать непереносимый код, если не быть начеку и постоянно не тестировать. И иногда правильным выбором в вашем случае будет переход на менее переносимый код, зависящий от конкретной среды.

Управление зависимостями

Или где именно находятся библиотеки?

Мы все в курсе, что язык C —  появился раньше сред, требующих работы с зависимостями и библиотеками.

Менеджер пакетов для всей системы

Это больше относится к Linux, но, один из стандартных способов, на который полагаются многие разработчики – использование общесистемного менеджера пакетов и доверие локальным дистрибутивам, в том, что они собрали и упаковали библиотеку, заголовочные файлы и любые дополнительные детали конфигурации в хорошо известном месте.

Например, если я хочу использовать библиотеку криптографии, то я могу установить libopenssl (а на большинстве систем также другие необходимые элементы сборки из libopenssl-dev) через командную строку и начать разработку своего приложения.

> sudo apt install openssl openssl-dev

У этого подхода есть и некоторые приятные преимущества: если вы распространяете свой код в виде исходного кода, вы можете собрать его и связать с установленным в вашей системе, и полагаться на функциональность, встроенную в версию, предоставленную вендором.

К сожалению, если вы используете более редкую библиотеку, более старую или более новую версию, чем поддерживается, или выполняете кросс-тестирование, или полагаетесь на определенные функции или свойства, которые упаковщик для вашей системы не предусмотрел, то вам, возможно, придется прибегнуть к другому методу.

Git-подмодули и сборка библиотеки как часть шага сборки

Ещё один способ управления зависимостями, который не зависит от платформы или специфического менеджера пакетов — это включение других проектов с помощью утилиты git submodule, описанной здесь. Этот метод особенно полезен, если вы не планируете часто обновлять код и в основном можете придерживаться определенного выпуска библиотеки.

Одним из недостатков является то, что вам, скорее всего, придётся собрать библиотеку хотя бы один раз, в зависимости от вашей системы сборки. Некоторые другие библиотеки могут столкнуться с проблемами при сборке таким способом, но большинство проектов на CMake значительно упростили этот процесс.

Ручной вендоринг

Еще один подход — просто взять исходный код библиотеки, от которой зависит ваша программа, и проверить его в своем коде. Некоторые библиотеки созданы специально именно для этого (например, Lua, SQLite amalgam). Если вы хотите добавить дополнительные возможности или скомпилировать код особым образом, и при этом не планируете часто обновлять версию, то этот метод может быть весьма полезным. Однако многие библиотеки не поддерживают такую интеграцию без дополнительных усилий.

Комментарии (67)


  1. Emelian
    06.01.2025 13:39

    Си, конечно, мощный язык, но С++ гораздо более дружелюбен и удобен. А Visual Studio C++, по факту, очень хорошая среда разработки.

    Я с Си всерьез столкнулся при попытке внедрить код FFPlay.c в своей проект «МедиаТекст» (он не опубликован, но скриншот можно посмотреть в http://scholium.webservis.ru/Pics/MediaText.png ). Но, чтобы добиться этого пришлось сильно напрячься. Вообще, перенос Си-проектов в С++ – удовольствие ещё то!


    1. segment
      06.01.2025 13:39

      Ну, разные языки под разные задачи. ИМХО если требуется что-то низкоуровневое, то я бы предпочел чистый C, а не C++, из-за читаемости и "отлаживаемости". Если требуется что-то более масштабное, гибкое и быстрое, то я бы взял C#. Все эти пляски со стандартами, перегрузками, sfinae и другая шаблонная магия — напрочь отбили желание как-то продолжать его использовать.


      1. Emelian
        06.01.2025 13:39

        Думаю, что лучше сравнивать собственные конкретные проекты. Для работы с пользовательским интерфейсом очень удобен C++ / WTL. Кстати, последний мой проект, в этой связке, это обучающая программа «L'école» ( https://habr.com/ru/articles/848836/ ).


        1. segment
          06.01.2025 13:39

          Не нашел по ссылкам исходники к программе, чтобы как-то посмотреть на удобство работы в коде. Для работы с пользовательским интерфейсом отлично подходит связка C# и AvaloniaUI.


          1. Emelian
            06.01.2025 13:39

            Не нашел по ссылкам исходники к программе, чтобы как-то посмотреть на удобство работы в коде.

            Вы можете показать мне скриншоты своей программы на Си либо С#. Мне этого будет вполне достаточно, чтобы сделать вывод: стоит с этим заморачиваться либо нет.

            А свои исходники я опубликую, когда оптимизирую код. Пока он мне самому не нравится, а вам, уверен, тем более. Там, сейчас, более актуальна концепция построения обучающих уроков, о чем, я надеюсь, поговорить позже.

            Для работы с пользовательским интерфейсом отлично подходит связка C# и AvaloniaUI.

            «Лучше один раз потрогать, чем сто раз увидеть» или что-то в этом роде… :) . Конкретно, меня интересуют опенсорсные исходники типа «ТоталКоммандер».


            1. Source
              06.01.2025 13:39

              ТоталКоммандер вообще на Delphi, насколько я помню, также как и классический Skype

              А идея оценивать удобство ЯП по скриншотам GUI звучит так, что вы знаете толк в извращениях. Тем более, что судя по вашим скриншотам, вы совсем какой-то древний UI-фреймворк используете. Даже 20 лет назад такой интерфейс выглядел устаревшим как какая-то программа времён Windows 95.


              1. Emelian
                06.01.2025 13:39

                ТоталКоммандер вообще на Delphi, насколько я помню, также как и классический Skype

                Делфи мне не интересен, хотя, в свое время, я программировал на полных исходниках ObjectProfessional, который тоже на Паскале.

                Меня интересует не именно TotalCommander, а приложения такого типа, но на С++. Например, опенсорсный файл-менеджер «Explorer++» (Exe-шник: https://download.explorerplusplus.com/dev/latest/explorerpp_x64.zip и Sources: https://github.com/derceg/explorerplusplus/tree/master ). Этот проект вполне устраивает меня, для моих целей.

                А идея оценивать удобство ЯП по скриншотам GUI звучит так, что вы знаете толк в извращениях.

                Не удобства ЯП, а его возможности. Вот если мне, например, не нравиться вести учет в веб-формах, то пусть они будут написаны, хотя на супер-пупер движке (вроде управляемых форм 1С8х). Достаточно, одного взгляда на скриншот, чтобы сделать вывод – это не моё!

                Тем более, что судя по вашим скриншотам, вы совсем какой-то древний UI-фреймворк используете. Даже 20 лет назад такой интерфейс выглядел устаревшим как какая-то программа времён Windows 95.

                Хорошо, покажите мне современный интерфейс, для задач моего типа, вроде «МедиаТекста» (ссылка на скриншот, здесь, в моем первом комментарии), либо обучающей программе «L'école». Только, не надо мне онлайновых программ, пусть они хоть сто раз будут современными. Только, оффлайн для десктопа, желательно, под «Форточки».

                Здесь уместно спросить: «Вам шашечки или ехать?». Я предпочитаю «ехать». Удобство моего интерфейса меня устраивает, было бы что-то лучшее, не писал бы своих программ, а взял бы готовые.


                1. PrinceKorwin
                  06.01.2025 13:39

                  Судя по скриншоту вам зайдет Tk, который поддерживается, пожалуй, всеми возможными языками уже.


                  1. Emelian
                    06.01.2025 13:39

                    Судя по скриншоту вам зайдет Tk, который поддерживается, пожалуй, всеми возможными языками уже.

                    Я видел, в Гитхабе, проекты на Питоне с графическим интерфейсов. Улыбнуло! Для питонистов это выход, но для «приплюснутых» есть лучшие варианты.


                1. Source
                  06.01.2025 13:39

                  Только, оффлайн для десктопа, желательно, под «Форточки».

                  Под «Форточки» вам .NET как эталон интерфейсов надо рассматривать, вот обзор UI-фреймворков под него: https://www.youtube.com/watch?v=ze8o-Qa3v7Y

                  Здесь уместно спросить: «Вам шашечки или ехать?». Я предпочитаю «ехать».

                  Это ложная дихотомия. Использовать современный UI-фреймворк ничуть не сложнее, чем UI-фреймворк старый как дерьмо мамонта. Наоборот, современные ещё и поудобнее будут.


                  1. Emelian
                    06.01.2025 13:39

                    Под «Форточки» вам .NET как эталон интерфейсов надо рассматривать, вот обзор UI-фреймворков под него: https://www.youtube.com/watch?v=ze8o-Qa3v7Y

                    Вы это серьезно? Мне приходилось, по работе, иметь дело с программами, разработанными на .NET. Ну, это же чудовищные монстры, для рафинированных садо-мазо. Уж, используйте его, как-то, без меня…

                    Это ложная дихотомия. Использовать современный UI-фреймворк ничуть не сложнее, чем UI-фреймворк старый как дерьмо мамонта. Наоборот, современные ещё и поудобнее будут.

                    Ага, Qt, например или, там U++ либо wxWidgets. Работал с ними, но для моих пет-проектов они чересчур избыточны, тем более, что всех задач интерфейса не решают. Например, хороших форм списков (в терминологии 1С), с группами, нет нигде. Хотя, ListCtrl, в MFC, поддерживает категории и группы, но как-то «через одно пикантное, не побоимся этого слова, импозантное место». Вот, разработаны, при царе Горохе, нормальные справочники в 1С77, с поддержкой групп и всё. Что-то более современного, с тех пор, не появилось. Еще худо-бедно присутствует иерархия в справочниках 1С81 и 1С82, но в 1С83, с их уклоном на управляемые формы, все стало только хуже. Лучшие из табличных контролов, представлены в Qt, но, во-первых, без групп (из «коробки»), а во-вторых, Qt – слишком громоздок. Тридцать лет прошло, но эталоном иерархических справочников остается только морально устаревшая «семерка», хотя как быстрой «рабочей лошадке» (когда надо разработать уникальную учетную систему еще вчера) конкурентов ей нет до сих пор.


                    1. Source
                      06.01.2025 13:39

                      Вы это серьезно?

                      Да, серьёзно. Интерфейсы под Windows определяет Microsoft. Чтобы они выглядели нативно, будьте любезны следовать их рекомендациям.

                      А что вы чудовищного нашли в .NET программах непонятно. Речь идёт об обычных простеньких десктопных приложениях. Там .NET очевидно лучший выбор под Windows. Кажется, вы явно переоцениваете на порядок сложность ваших пет-проектов, раз считаете что C++ там жизненно необходим.

                      P.S. Почитайте книгу "Психбольница в руках пациентов". Там как раз подробно разбирается ситуация, когда программисты делают интерфейсы для себя, а не для пользователей.


                      1. Emelian
                        06.01.2025 13:39

                        Интерфейсы под Windows определяет Microsoft.

                        Ну, да. WTL, например. Чем он вам так не нравится? Потому, что «древний»? Зато простой, для пет-проектов, само то!

                        Чтобы они выглядели нативно, будьте любезны следовать их рекомендациям.

                        Для WTL, я и следую рекомендациям. Или M$ уже ввела запрет на использование своих продуктов, условно говоря, раннее 2020-года?

                        А что вы чудовищного нашли в .NET программах непонятно.

                        Ну, я же говорил, вынужден был иметь дело, на работе, с коммерческими .NET программами. Это и были «монстры», при том, что пользовательский интерфейс там был ужасный. Я бы переписал функционал тех программ на 1С77, только там были «раненые на голову» системы шифрования / дешифрования никому не нужной информации. При этом экспорт / импорт, нужной нам информации, штатными средствами не предусматривался. Приходилось, чуть ли не хакерским методами экспортировать туда наши данные, поскольку вводить их в тех программах было попросту невыносимо. А когда избавились от этой обязаловки, был хороший повод отметить «это дело».

                        Речь идёт об обычных простеньких десктопных приложениях.

                        А кому они нужны, если они ничего не умею делать?

                        Там .NET очевидно лучший выбор под Windows.

                        Мне, совершенно не очевидно! А, очевидно, С++/ WTL.

                        Кажется, вы явно переоцениваете на порядок сложность ваших пет-проектов, раз считаете что C++ там жизненно необходим.

                        Ну, я бы понял разговор, если бы были представлены аналоги моих программ «МедиаТекст» и «Леколь» в других системах разработки. Вся дискуссия идет «вообще», а не «конкретно». А это, никому, ничего не доказывает. Каждый, в любом случае, останется при своем мнении.

                        Там как раз подробно разбирается ситуация, когда программисты делают интерфейсы для себя, а не для пользователей.

                        Ну, и в чем проблема? Я лично, ничего не имею против любого творчества. Если мне что-то не нравится, то, просто не использую это, без желания «перевоспитать» кого-либо. Другое дело, когда принуждают заниматься нелюбимым делом. Скажем, работать с «дурацкой» программой. Тогда стараюсь найти какой-то приемлемый для себя выход. И, чаще всего, нахожу.


                      1. Source
                        06.01.2025 13:39

                        Ну, у вас свой путь - хобби-программирование.

                        В коммерческой разработке свои правила. Там не прокатит применять интерфейсы 30-летней давности. Разве что это заказная разработка для какой-то мелкой конторы, где вы демпингом контракт заполучили и им теперь сложно отказаться.


                      1. Emelian
                        06.01.2025 13:39

                        Ну, у вас свой путь - хобби-программирование. В коммерческой разработке свои правила.

                        Я ведь не претендую на свой «Устав» в коммерческой разработке. Просто, то «коммерческое», с чем приходилось сталкиваться на основной работе, мягко говоря, не очень. Сейчас основная тенденция это веб-программирование, работа с онлайн-формами обмена данными и т.п. По удобству пользования все это не ахти. Производственный учет, в предприятиях, переводят на управляемые формы «восьмерки».

                        Для меня это все скучно и не интересно. Потому и пошел по пути собственных пет-проектов. Удивляет только менторский тон собеседников. Мол, программирую «неправильно», Надо применять Qt, Net. TK и далее перечисляют всё, что нравится («вообще», а не «конкретно», т.е., без показа собственных наработок) участникам дискуссий. Не понимая, что человек просто решает задачу, которая у него возникла. Как решил, показал свой проект. Не нравится, не пользуйтесь, какие проблемы? Нет, надо «поучить демократии» или чему там еще?

                        Там не прокатит применять интерфейсы 30-летней давности. Разве что это заказная разработка для какой-то мелкой конторы, где вы демпингом контракт заполучили и им теперь сложно отказаться.

                        Я использую, в основном, C++ / WTL. C++ более чем 30-летней давности. Отменяем его? WTL первой версии уже лет двадцать, а последняя, десятая версия, обновлена в 2020 году. Тоже – слишком старая? А у капиталистов новое, не всегда лучше, но, как правило, сложнее и дороже. Так стоит ли заморачиваться возрастом фрейморка, если он реально позволяет решать задачи? И не за счет пользователей, которым снижают удобство работы ради Интернет-мобильности приложений для них.

                        Поэтому, если меня попросят назвать самую лучшую современную программу для пользователя, допустим, ведущего учет на предприятии, то я даже не знаю, что сказать. ERP на 1С83? – Эта система хороша для фирмы «1С» (приносит им максимальную прибыль), но, «так себе» для рядового пользователя. Что еще? А ничего! Учет в облаке – того же порядка.

                        Хорошо. Какие можно назвать современные продукты от российских программистских команд, которыми бы хотелось пользоваться простым смертным? Что-то я, «с разбегу», не могу назвать ни одной. Может быть, вы подскажите? Я не имею в виду игры.


            1. rukhi7
              06.01.2025 13:39

              Вот можете скачать потрогать и код тоже посмотреть:

              https://github.com/libreoffice

              интересно узнать ваши впечатления, если вы действительно ищете примеры пользовательского интерфейса в исходниках.


              1. Emelian
                06.01.2025 13:39

                Вот можете скачать потрогать и код тоже посмотреть: https://github.com/libreoffice интересно узнать ваши впечатления, если вы действительно ищете примеры пользовательского интерфейса в исходниках.

                Ну, с такими ссылками я и сам имею дело по многу раз в день. Это «вообще», а я предпочитаю говорить о «конкретном».

                Вот я дал выше ссылки на «Explorer++» :
                Exe – https://download.explorerplusplus.com/dev/latest/explorerpp_x64.zip
                Code – https://github.com/derceg/explorerplusplus/tree/master .

                Запускаем программу и сразу понятно, о чем речь. После этого, при наличии интереса, скачиваем исходники.


                1. rukhi7
                  06.01.2025 13:39

                  Это «вообще», а я предпочитаю говорить о «конкретном».

                  из конкретного есть WPF (над DirectX-м там все просто и понятно как по мне), не пробовали? Там даже исходники есть в открытом доступе, как ни странно. Видимо они потому и в открытом доступе, потому что джентельменов "чужие" технологии не интересуют, они изобретают свои с приставкой нано.


                  1. Emelian
                    06.01.2025 13:39

                    из конкретного есть WPF (над DirectX-м там все просто и понятно как по мне), не пробовали?

                    Я не понимаю вашей логики. Вам хочется, чтобы я для решения своей задачи применил технологию, которая интересна вам? Но, ведь задача уже решена, зачем мне ее решать повторно? Чтобы вам было бы приятней пользоваться этой программой? Но, не уверен, что вы будете пользоваться моей программой, в любом случае.

                    джентельменов "чужие" технологии не интересуют, они изобретают свои с приставкой нано.

                    «Жентельменов»,, как и «Мадамов», «чужие» технологии интересуют. У меня там из своего только весьма специфические алгоритмы, для которых нет фрейморков. А так, WTL – фрейморк от M$, кроме того применен опенсорс по работе с графикой, звуком и перегрузкой видов в SDI-окне. Кроме того, использован опенсорсный движок SQLite. Без них бы, я эту программу не потянул бы.

                    Конечно, есть и другие технологии, с помощью которых можно решить эту задачу. Но нет смысла применять их все сразу. Если вы считаете, что технология А эффективней технологии Б, то обосновывайте свой вывод, желательно на примерах, а не голословно.

                    Ладно, вот уже много лет меня интересует контрол, типа, «справочник» в 1С77, который поддерживает группы. Можете предложить опенсорный проект по использованию аналогичного контрола? Например, как в ТоталКомандере используется отображение папок и файлов. Мне нужно тоже самое, но для базы данных, например, SQLite.

                    Если вы не подскажите мне ответ, то придется изобретать собственный велосипед, с «приставкой нано». Чтобы потом, вы опять сказали, ну, вот, снова не применил существующие технологии.

                    Кстати, без групп, можно использовать (на С++ / WTL) исходники контрола «WTLGridCtrl». Это лучшее, что я знаю, для WTL.


                    1. rukhi7
                      06.01.2025 13:39

                      Вам хочется, чтобы я для решения своей задачи применил технологию, которая интересна вам

                      да нет, я не конкретно про вашу задачу писал, а как бы в общем, если уже работает проект и, видимо, достаточно большой его конечно не перепишешь вдруг.

                      меня интересует контрол, типа, «справочник» в 1С77, который поддерживает группы. Можете предложить опенсорный проект по использованию аналогичного контрола?

                      так вам не использование контрола надо искать, мне кажется, а "Проект контрола" - библиотеку с контролом какой вам нужен или хотя бы такой чтобы его не сложно было доработать до вида который вам нужен, наверно можно кстати и WPF-ные контролы посмотреть, их насколько я знаю из С++ можно вызывать, но с этим наверно сложно разбираться, это какая-то отдельная супер технология в которую вдруг не влезешь конечно, мне бы самому наверно месяц-два понадобился чтобы что-то изобразить в этом направлении работающее. Наверно самое сложное с визуальными компонентами это их согласование с текущим рабочим енвайроментом (с действующи набором библиотек). Ну и разработка контролов это отдельная тема от их использования.


                      1. Jijiki
                        06.01.2025 13:39

                        если спуститься по ниже на WinApi будет разработка почти как в SDL любой версии, в ВинАпи в части отрисовки окна, кистей, отрисовке текстур через GDI(на сколько помню есть еще помню есть конкретно свои методы по загрузке текстур и дескрипторов текстур), RichText и прочее, сами удивитесь будет даже проще чем даже если и намерено рисовать в граффике в DirectX любой версии, я про десктопные приложения аля Нотпад и прочее


                      1. Emelian
                        06.01.2025 13:39

                        да нет, я не конкретно про вашу задачу писал, а как бы в общем, если уже работает проект и, видимо, достаточно большой его конечно не перепишешь вдруг.

                        На самом деле переписать проект не проблема, главное, чтобы был смысл. Так, я уже думаю над второй версией «МедиаТекста», поскольку, при работе с ним, столкнулся с необходимостью его улучшения.

                        Поэтому, я был бы не прочь узнать, что именно хотят другие, в части программного интерфейса. Однако понять претензии довольно сложно. Похоже на анекдот:

                        «На митинге Партии Женщин:
                        – Кто мы?
                        – Женщины!
                        – Чего мы хотим?
                        – Не знаем!
                        – Когда мы этого хотим?
                        – Прямо сейчас! И много!!!»

                        Мы ведь говорим на разных языках. То, что для меня означает «вообще», для вас означает «конкретно». И тому подобное.

                        кстати и WPF-ные контролы посмотреть

                        Да, нет там ничего! Например, в https://github.com/Kinnara/ModernWpf - обычные списки, без групп. А сам интерфейс «плоский», который мне не нравится. Причем, главное, это проект на C#, что вне моих интересов. Я предпочитаю оставаться на С++.

                        Наверно самое сложное с визуальными компонентами это их согласование с текущим рабочим енвайроментом (с действующи набором библиотек). Ну и разработка контролов это отдельная тема от их использования.

                        Да, нет особого смысла впихивать чужеродные контролы в свой проект. Лучше уже разрабатывать контролы самому. В моем случае, есть подходящий прототип WTLGridCtrl, о котором я уже упоминал. А группы ему можно будет уже нарисовать самому. Кстати, все контролы это именно умение рисовать их на экране компьютера. Поэтому, надо хорошо осваивать работу с графикой, вроде GDIplus и ему подобным, скажем, с опенсорсной библиотекой «ImageStone».

                        Для примера, смотрите скриншот моих редактируемых ячеек ( http://scholium.webservis.ru/Pics/CellsEdit.png ), которые я успешно рисую в своей демонстрационной программе (там можно редактировать любую ячейку, перемещая фокус ввода).


                      1. rukhi7
                        06.01.2025 13:39

                        Кстати, все контролы это именно умение рисовать их на экране компьютера.

                        я бы даже сказал управлять рисованием на экране или даже программировать рисование. И я согласен что:

                        Поэтому, надо хорошо осваивать работу с графикой, вроде GDIplus и ему подобным, скажем, с опенсорсной библиотекой «ImageStone».

                        наверно хороший пример, с которым я не очнь знаком. Для меня WPF является такой своего рода опенсорсной библиотекой. Но в основе любой такой графической библиотеки лежит определенная концепция-идея, которую требуется время понять осознать, в этом сложность если мы имеем и занимаемся в какой то выбранной (задданной проектом) системе даже просто для сравнения попробовать что-то другое очень непросто затратно. Согласен!


        1. eugenk
          06.01.2025 13:39

          Пользовательский интерфейс я сейчас делаю исключительно в браузере, и более никак. Практика показала, что этот способ наиболее переносим.


          1. 4chemist
            06.01.2025 13:39

            ... только необходимо изучить язык разметки html, изучить стили css, ну и javascript. Учитывая их "совершенно не строгую типизацию" переменных, разработка интерфейса скатывается в "добавил свойство в css, перегрузил страницу в браузере, с удивлением обнаружил, что свойство не применимо в текущем контексте". Лыко и мочало начинай с начала. Но наше "наиболее" перевешивает все.


            1. eugenk
              06.01.2025 13:39

              Есть куча языков, компилируемых в js. Тот же TypeScript например. Хотя я предпочитаю scala.js. Пожалуйста тебе и строгая типизация и ещё куча вкусных плюшек. На js пробовал писать лет 12 назад. Более 2К строк не выдержал. Хорошо добрые люди подсказали мне про TypeScript который тогда только-только появился.


      1. wigneddoom
        06.01.2025 13:39

        ИМХО если требуется что-то низкоуровневое, то я бы предпочел чистый C, а не C++, из-за читаемости и "отлаживаемости".

        Можно всегда на С++ в Си-стайл писать, используя только подмножество языка. Но у Си конечное есть своя ниша - старые/актуальные и новые проекты, типа Linux/DPDK/SPDK/кодеки/либы и т.д. Ну и конечно C ABI.


        1. segment
          06.01.2025 13:39

          Тут дело в том, что на C++ можно писать сильно по-разному. Кто-то плотно сидит на boost, у кого-то свое понимание использования шаблонов, бывает магия перегрузок и так далее. А бывает, как Вы сказали, и в стиле Си с классами. И загвоздка может заключаться в том, что в разных проектах C++ может использоваться кардинально разный подход и области языка. В случае Си или C# я не видел подобного, обычно все довольно похоже и целостно (за исключением добавления странного на мой взгляд сахара в C# в последние года). Это исключительно моё наблюдение, не претендую на объективность. Отдельно добавлю, что я с удовольствием смотрел лекции Константина Владимирова по C++, но это скорее академический интерес, мне немного возвращает воспоминания из юношества, когда я изучал плюсы и сделав тогда невероятно хитрый шаблон который разворачивался из одной строчки во что-то безумное — был невероятно горд собой и получал от этого удовольствие, но вот в работу я бы такой код не добавлял =)


          1. Rezzet
            06.01.2025 13:39

            Писать можно, но нет, не пишут сильно по разному, все пишут примерно одинаково, есть "талантливые" ребята которые могут в шаблонную магию высокого порядка, но таким говоришь не пиши так и они не пишут. В среднем народ пишет на с++ как на си с классами и немного простых шаблонов + стандартная библиотека, типа возврата optional, если подразумевается что значения может не быть. 9 из 10 программистов в шаблоны толком не умеет. А насчет плотно сидит на boost, что в вашем понимание плотно сидеть? boost достаточно разношерстная и огромная библиотека, ее части можно использовать независимо от друга, но не в этом суть, разработка проекта ведется командой, команда должна обсуждать какие библиотеки использовать и брать в проект, в крайнем случае это может сказать лид, это юзаем, это не юзаем, но для этого у лида должен было очень сильный авторитет в команде, иначе будет разброд и шатание, плюс саботаж решений. я предпочитал советоваться с командой, точнее спрашивать у них и провоцировать их самих прийти к решению, если команда в целом подобрана правильно и сработавшаяся то мнение как правило единогласное. А тем кто приходит на проект, ну сорян, есть проект, есть зависимости, что бы хорошо ориентироваться в проекте придется подразобраться с зависимостями то же. Но как правило большой проблемы нет, есть 20-30 распространённых либ, их примерно все и используют. Так на вскидку, для сети curl, для работы с сжатием zlib, картинки imageio, физика libbullet, бд libpq или sqlite, и так далее... или у вас Qt и там все и так есть на все случаи жизни и можно ни о чем не думать.


            1. dv0ich
              06.01.2025 13:39

              В среднем народ пишет на с++ как на си с классами и немного простых шаблонов + стандартная библиотека

              Так а зачем городить сложности на ровном месте?

              Впрочем, писать на плюсах, как на сишке (с сырыми указателями и прочими штуками) не очень хорошо. На плюсах надо писать как на плюсах. Но и наворачивать тоже ни к чему. Я, например, редко использую исключения и шаблоны - только там, где без них реально будет сложнее и очень многословнее.


              1. Rezzet
                06.01.2025 13:39

                Мне не нравится как сделаны исключения в с++, их не использую и всем советую делать так же. Только из конструктора нельзя вернуть ошибку, но здесь проблема решается просто, раздели инициализацию и конструирование объекта и возвращай ошибку другими способами.

                Зачем городить сложности, так и я говорю что их не городят. А пишут на с++ примерно как на яве или сишарпе. Сырые указатели заворачиваются в unique_ptr. Сложно так сказать сразу, как-то выработался за время работы некий джентельменский набор хорошего тона что стоит использовать что нет. На си нет такой хорошей стандартной библиотеки, потоков, да много чего нет, filesystem, thread и прочего и прочего. А самое важное что на си нет многих библиотек, сам язык не так важен, наверно многие библиотеки имеют си интерфейс, например curl но использовать чистый курл - да застрелиться можно, cpr значительно удобнее. Си библиотеки можно в с++ обратно сложно. С++ силен огромный количеством библиотек в первую очередь, меня всегда удивляют рассуждения на тему вот тут функцию вызвать удобнее, а тут массив перебрать, да вообще все равно как оно там и какой в языке синтаксический сахар. На С++ я могу решать любую прикладную задачу и скорее всего там будет лучшая на свете библиотека для этого. В других языках будет в лучшем случае порт си/с++ библиотеки в их язык через обертки. Но некоторые библиотеки я не представляю как портировать boost::graph или CGALL, особенно в последней просто тысячи человек работы закопаны, конечно она не всегда нужна или даже правильнее сказать очень редко и мало кому нужна, но если вам понадобится то где это взять? Вообще перечислять библиотеки можно часами, так на вскидку Bullet, OpenCV, OpenImageIo, Imgui, libpq, libnoise, box2d, spirv-tools, taskflow, sqlite и я еще не достал ультимативное оружие в виде Qt.

                Почему-то все думают что с++ такие отмороженные и вообще не хотят даже смотреть в сторону других языков, да почему не хотим, я лично прикручивал python, lua, chaiscript к разным проектам на с++, все хорошо там где оно уместно, просто я реально не понимаю зачем мне идти и разбираться в концепции AndroidSDK, если я могу взять и сделать приложение на с++, прикрутить к нему imgui и программировать себе дальше ровно так же как до этого делал на десктопе все кнопочки, менюшечки и прочее, ну или Qt достать и делать все то же самое. Это же справедливо и под iOS. Про десктоп вообще молчу, с 2017 года наверно даже стало как-то некультурно обсуждать будет ли кроссплатформенная поддержка или нет, просто сразу думаешь о том что приложение будут собирать на вин, мак, линуксе.

                Просто два слова которые уложат на лопатки любые попытки заменить с++. ffmpg и gstreamer. Все нет аналогов. В лучшем случае порты. И не будет никогда. Никто не напишет на java аналог gstreamer. Надо для этого еще одну планету иметь с 8 млрд населением.


                1. unC0Rr
                  06.01.2025 13:39

                  два слова которые уложат на лопатки любые попытки заменить с++. ffmpg и gstreamer

                  Но они же на Си?

                  Никто не напишет на java аналог gstreamer.

                  Разработчики gstreamer усиленно переходят на раст. Ядро фреймворка, может быть, ещё долго будет сишным, но плагины на расте пишут.


                1. Jijiki
                  06.01.2025 13:39

                  если нет интереса писать свои реализации физики и посмею сказать на библиотеки окон запустить окно иксами извините или ВинАпи, и в альтернативу воспользоваться 1 библиотекой для текстур и 1 библиотекой для музыки, то вполне норм, если брать по минимуму то матешу, окно, и часть физики вполне вроде можно осилить, даже учитывая что есть мастодонты Анриал и Юнити (единственное препятствие может быть если нужен Андроид), насчет имгуй я пока не понял почему его все хвалят вы же можете сделать двух матричную отрисовку и гуй рисовать решив еще вопрос со шрифтами (ну разве что сложность может быть с реализацией BVH - но вроде можно натаскаться поняв суть зачем он - отсюда баундинг боксы(причем они просто симулируются для расчета) и рейкасты)


          1. fujinon
            06.01.2025 13:39

            Из предисловия к одной популярной книге по C++ :

            С++ — это на самом деле не столько язык, сколько инструмент для создания ваших собственных языков.


            1. IUIUIUIUIUIUIUI
              06.01.2025 13:39

              Разве что запрещением тех или иных возможностей в вашей кодовой базе. Ну как где-то запрещены исключения, где-то запрещено шаблонное метапрограммирование, где-то ещё что запрещено.

              Создавать языки метапрограммированием в C++ тяжело: адекватной макросистемы нет, шаблонное метапрограммирование — ад. В языке ML-семейства (в хаскеле, например) куда проще что автору библиотеки с eDSL'ем, что её пользователю. Лисперы тоже много чего хорошего расскажут про их стиль метапрограммирования.

              Создавать языки «классически», где вы пишете парсеры-компиляторы-интерпретаторы, на C++ тоже тяжело хотя бы просто потому, что на том же хаскеле комбинаторные парсеры пишутся за время, сравнимое со временем однократной компиляциии проекта, использующего тот же boost.spirit.

              Я бы сказал, что основная ниша плюсов — развитие и взаимодействие с уже написанным кодом на плюсах. Так как его много, плюсы никуда не денутся. Но начинать новый чистый проект в 2025-м на плюсах я не вижу смысла (если это, конечно, не какая-нибудь эмбеддщина, где компиляторов других языков нет).

              Правда, к C почти всё это тоже относится.


              1. fujinon
                06.01.2025 13:39

                Я эту цитату про "собственные языки" менее буквально понимаю, в том примерно смысле как было выше написано что "на C++ можно писать сильно по-разному" , то есть для конкретного проекта вы создаете какие-то свои локальные мини-идиомы.
                А с практической точки зрения я с Вами вынужден согласиться, я люблю C++, но теперь это только как хобби...


            1. duke_alba
              06.01.2025 13:39

              Я бы сказал, что С++ допускает большую свободу в определении своего стиля программирования. Но не создания языка.


      1. Starl1ght
        06.01.2025 13:39

        мы несколько драйверов написали на С++17. Это просто небо и земля по сравнению с чистым Си. Деструкторы, шаблоны, лямбды. Я вообще не представляю зачем писать на С в 2025 году хоть где-то.


        1. segment
          06.01.2025 13:39

          А есть ли возможность посмотреть на код где-то? Или может быть у Вас есть ссылка на нечто похожее, которое отражает суть вышесказанного "небо и земля"?


          1. Starl1ght
            06.01.2025 13:39

            Клосед сорс, само собой, но скажем так, из простого - у нас был самописный аналог std::vector и std::string, это уже делает С++ на порядки проще и понятнее, чем чем ручные реаллокации массивов и UNICODE_STRING (в виндовом кернеле). Ну и без утечек, т.к. деструкторы.


        1. glazzkoff
          06.01.2025 13:39

          Согласен, лучше писать на C++ и при необходимости делать обёртку на C для FFI.


    1. eugenk
      06.01.2025 13:39

      Вот не знаю... Чистый Си очень прост. И глядя на код довольно легко понять, во что примерно он будет развернут. Глядя на код С++, этого понять уже нельзя. В приложениях реального времени (а мне раньше часто приходилось этим заниматься) это очень важное качество. Я честно говоря вообще не понимаю, зачем С++ до сих пор развивают и принимают новые стандарты. Он уже стал совершенно необозрим, и напоминает скорее собрание собрание заплаток и костылей. А для более абстрактного и высокоуровневого программирования сейчас есть языки куда более приятные чем С++. По-моему зафиксировать его на текущем стандарте и забыть.


      1. brownfox
        06.01.2025 13:39

        Чистый Си очень прост. И глядя на код довольно легко понять, во что примерно он будет развернут.

        Ну, это как писать :)

        Особенно, с учётом возможностей препроцессора. Реально, на практике астречается много "заумно эффективного" кода, особенно, в программировании микроконтроллеров


        1. eugenk
          06.01.2025 13:39

          Не, ну тут как бы без вопросов. Говнокод можно написать на любом языке, тут нет ничего удивительного. Просто по-моему на С++ сделать это значительно легче, чем на чистом С. Разумеется я про задачи реального времени, где очень важно иметь представление, во что код будет развернут. Если оно будет ходить на столе у бухгалтера, там разницы особой нет. Лишь бы работало. Но для этого ни С не нужен, ни С++.


      1. Emelian
        06.01.2025 13:39

        Чистый Си очень прост. И глядя на код довольно легко понять, во что примерно он будет развернут. Глядя на код С++, этого понять уже нельзя.

        Ну, это ни о чём не говорит. Вот беру я код FFPlay,c, размером около 150 КБ. И что я там вижу? Вроде, достаточно простой алгоритм использования различных функций и структур (порой чрезмерно сложных, вроде OptionDef options[]). Но, во-первых, никто не отменял зависимости, а они, для консольного видео-плейера, достаточно сложны. А, во-вторых, мне не нужны консольные программы, я больше ориентируюсь на оконные приложения. Поэтому, чтобы интегрировать видео в отдельное окно, мне приходится делать достаточно много телодвижений.

        Что касается «кода С++». Да, любой объемный проект, достаточно сложен для восприятия. И подчас, чтобы просто скомпилировать его, надо иметь чуть ли не «семь пядей во лбу», особенно, если система сборки не связана с Visual Studio C++ и, тем более, с «Windows». Но, здесь я использую метод «пересборки» проекта. Т.е., делаю пустой проект и начинаю туда последовательно копировать код, из оригинального проекта, в порядке его выполнения. Это, конечно, долго. Но, я руководствуюсь такой установкой. Допустим мне надо написать подобную программу с нуля. Вот, у меня есть рядом аналогичный проект, как прототип. Ведь, все же легче программировать по прототипу, даже со всеми его «бзиками», чем, полностью, с нуля. При достаточной настойчивости, иногда, получается, что очень радует.

        Далее, если собственный проект, на С++, полностью оригинальный, то он вполне понятный, поскольку, реализуется постепенно по нужному сценарию. Здесь, естественно, имеет смысл использовать паттерны «идеального программирования». На эту тему много всякой разной литературы и вопрос еще не закрыт. Для себя я придерживаюсь такой тактики:

        1. Использую шаблоны WTL, на мой взгляд, идеального фреймворка, для моих целей.

        2. Применяю метод «итерационного программирования». Т.е., нулевой проект это пустой шаблон приложения, например, сгенерированный мастером WTL и «причесанный» на собственный вкус. В первой итерации добавляется небольшой код, с описанием, в сопровождающем его, doc-файле. Во второй итерации проекта добавляется еще один кусок кода, делающий какую-то минимально самодостаточную работу и т.д. и т.п. Не могу сказать, что это идеальный подход, но, я постоянно думаю, над его совершенствованием. Например, выношу общую часть всех итераций в отдельный каталог.

        Я честно говоря вообще не понимаю, зачем С++ до сих пор развивают и принимают новые стандарты. Он уже стал совершенно необозрим, и напоминает скорее собрание собрание заплаток и костылей.

        Новые стандарты, как и новые версии Виндоуз, я, лично, просто не использую. Просто, нет потребности. Однако, сулить нужно / не нужно, не берусь. «Им» из «погреба» виднее. Пока, мне более, чем достаточно VS C++ 2013 и 2017. Что-то есть ценное, в плане создания проектов из «чистого» кода в VS-2019, который я использовал только затем, чтобы создать проектные файлы, в которых уже понижал версию «Студии» до 2013-й или даже до 2010-й. Ограничением здесь являются только новые фичи языка, которые встречаются в опенсорных проектах. Всегда стараюсь, по мере возможности, от них избавиться


        1. brownfox
          06.01.2025 13:39

          В 20-м C++ ввели стоковые шаблоны а-ля f-строки в питоне :)


    1. uuger
      06.01.2025 13:39

      можно посмотреть в http://scholium.webservis.ru/Pics/MediaText.png

      Ого, чистый незамутнённый http в 2025 году


  1. Uporoty
    06.01.2025 13:39

    Странно, что в выжимке "что обязательно нужно знать" ни слова не сказано про Undefined Behavior.

    А это именно то, что нужно любому разработчику на сях и плюсах знать с первых дней. Потому что доходит до откровенно смешного, я не раз сталкивался, что люди, которые пишут код на C много лет (!), не до конца понимают, что же такое undefined behavior и чем оно может грозить - и продолжают распространять всякие заблуждения, что "это только про ошибки работы с памятью", и "если на моей платформе и моем компиляторе оно работает, то значит так можно, все нормально"...


    1. Serpentine
      06.01.2025 13:39

      Странно, что в выжимке "что обязательно нужно знать" ни слова не сказано про Undefined Behavior.

      В статье есть раздел (прямо в оглавлении указан под №14 из 15):

      14. Переносимость, неопределенное поведение и безопасность доступа к памяти


  1. jaha33
    06.01.2025 13:39

    А в каком языке нет проблем с библиотеками и зависимостямм? Например на питоне могут не менее интересные чудеса. Как то натыкался на случай, когда питоновский скрипт требовал библиотеку определенной версии, а автор библиотеки эту версию удалил из общего доступа.

    Уверен и на шарпе что то похожее может происходить


    1. Auerd
      06.01.2025 13:39

      На питоне всё-таки есть собственный менеджер пакетов pip, что гораздо удобнее нежели скачивание пакетов вручную или добавление скачивания через сценарии. Просто две команды: pip freeze > requirements.txt и pip install -r requirements.txt


    1. 4Droidek
      06.01.2025 13:39

      В питоне есть несколько вариантов. Либо попытаться вручную решить конфликт версий путём доработки модуля(если не бинарник). Или найти на PyPI аналог.


    1. MountainGoat
      06.01.2025 13:39

      С Питоном не смешивайте, там свой мир. Я наткнулся на багу в opensource библиотеке, которая мешала. Поправить то можно, но софт должен идти на конкретном дистрибутиве конкретной версии, и там пакет библиотеки уже не обновится. Что делать? Всю библиотеку с собой тащить?

      Нет, гружу как есть и после загрузки подменяю в загруженном коде один метод на свой, поправленный.

      Как такую проблему решают в С++?


      1. boris768
        06.01.2025 13:39

        Обычно тянут свой подправленный вариант в виде динамической библиотеки, под windows такая практика повсеместна, под linux для closed-source плюс-минус тоже.

        Хотя в целом, ничего не мешает поставить хук на таком методе и выполнить свой вариант, но конечно же это удовольствие совершенно не из легких. Но так можно решить проблему в любом нативном коде, не только C++.

        А вообще если это действительно баг - отправлять PR конечно же.


  1. 4Droidek
    06.01.2025 13:39

    Когда впервые начал изучать, то взял с++. Но по истечении времени решил, что более правильно будет начать с си. Во-первых он проще, а во вторых очень полезно наработать чтение си, т.к. много всякого опенсорса есть на нём. Первой моей целью разбор CPython. Но это вкусовщина.


  1. Jijiki
    06.01.2025 13:39

    помимо gnu ld, есть еще gold и mold, сейчас переписываю 3D(DSA OpenGL 4.6 core) на С без использования библиотек (использую только такие библиотеки как X11,GLX, Glew, assimp, stb) и столкнулся, что ld действительно медленный, но в целом и так нормально покачто (компил без стандарта - мне нравится как работает, структурный стиль очень удобный), без симейка в вскод с простым скриптом сказка просто, после этих всяких перелинковок Симейка, программа на С немного по другому работает, еще заметил окно быстрее открывается - практически мгновенно


  1. LAutour
    06.01.2025 13:39

    Из IDE можно еще вспомнить Code::Blocks: выполняется в системе наитивно и нетребовательна к ресурсам, в отличии от перечисленных в статье.


    1. Parmenides
      06.01.2025 13:39

      Плюсую.

      Ещё KDevelop и Qt Creator.

      А Visual Studio Code - обдолбавшийся беременный бегемот, а не IDE.


  1. LAutour
    06.01.2025 13:39

    Одно из преимуществ всего этого — частичная сборка: если вы сохраняете объектные файлы после компиляции чего-либо, вам нужно перекомпилировать объектные файлы только для тех C-файлов, которые вы обновляете.

    Это если вы не изменяете общие .h файлы, включаемые в исходники и желательно вручную указываете что компилировать. На С\С++ приходится для микроконтролеров и сильно раздражает, когда меняя один .h файл включаемый тоьлко в один из .С файлов, чертова автоматическая сборка иногда начинает полную перекомпиляцию вообще всего.


  1. lrrr11
    06.01.2025 13:39

    Язык C небезопасен с точки зрения доступа к памяти, плюс вам придется самостоятельно управлять ею

    что это вообще значит? Если процесс начнет ломиться куда не положено, то получит SEGFAULT. Или например после fork() процесс-потомок не получит доступ на запись к памяти родителя без дополнительных усилий. Это безопасность или нет? А как насчет присутствующих в gcc и clang аттрибутов cleanup, constructor, destructor - это случайно не полные аналоги init и defer из golang?

    Когда вы работаете с кодом, который должен обезопасить или обработать ненадежные входные данные

    то есть в других языках входные данные всегда надежные, ничего обрабатывать не надо? Дальше не читал, прокрутил вниз.

    Мы все в курсе, что язык C — появился раньше сред, требующих работы с зависимостями и библиотеками.

    я в курсе, что например в cmake есть FetchContent, позволяющий скачивать что угодно откуда угодно. А ламер, наливший кучу воды в этой статье - видимо нет. Хотел написать "наливший кучу воды при помощи нейронок", но даже ChatGPT в ответе на вопрос про управление зависимостями в проектах на Си упоминает conan с vcpkg...


    1. wadeg
      06.01.2025 13:39

      Хотел написать "наливший кучу воды при помощи нейронок"

      И затем это полито каким-то адским брейнфакингпромптом:

      Например, если я хочу использовать библиотеку криптовалют, то я могу установить libopenssl

      Оригинал:

      For instance, if I want to use the cryptology library, then I can pull install libopenssl


    1. Noah1
      06.01.2025 13:39

      Если процесс начнет ломиться куда не положено, то получит SEGFAULT.

      Это и есть "небезопасная работа с памятью". Если указать выходящий за пределы массива индекс в С - будет segfault, всё упадет, мир взорвется. В более безопасных языках есть сначала compile-time check , потом runtime check, после которого будет либо ошибка, либо исключение, а не краш.


      1. lrrr11
        06.01.2025 13:39

        В более безопасных языках есть сначала compile-time check

        int main(int argc, char *argv[]) {
          int a[5];
          a[6] = 1;
        }
        
        $ clang t.c
        t.c:3:3: warning: array index 6 is past the end of the array (that has type 'int[5]') [-Warray-bounds]
            3 |   a[6] = 1;  
        

        это компайл-тайм чек?

        потом runtime check

        тебе кто-то запрещает делать рантайм проверки в Си, бездарь?

        после которого будет либо ошибка, либо исключение, а не краш.

        ты несешь бессмысленную чушь. Рассказать, к чему приводит необработанное исключение, или сам догадаешься?


        1. IUIUIUIUIUIUIUI
          06.01.2025 13:39

          это компайл-тайм чек?

          Это хрупкая эвристика, которая тривиально ломается:

          void set(int *arr, int pos, int val)
          {
              arr[pos] = val;
          }
          
          int main()
          {
              int a[5];
              set(a, 6, 1);
          }

          $ clang++ -Wall -Wextra test.cpp
          $

          тебе кто-то запрещает делать рантайм проверки в Си, бездарь?

          Бездарям из кучи опенсорс-проектов, в которых потом находятся CVE, что-то мешает.

          Рассказать, к чему приводит необработанное исключение, или сам догадаешься?

          Откуда подмена тезиса про «необработанное» взялась?


      1. IUIUIUIUIUIUIUI
        06.01.2025 13:39

        Если указать выходящий за пределы массива индекс в С - будет segfault, всё упадет, мир взорвется.

        Это лучший случай. Худший случай — это если оно пройдётся по памяти вашего же процесса, и просто тихо испортит данные.


  1. Funny_panda
    06.01.2025 13:39

    А есть ли такая статься по JS?)


  1. gorod0k
    06.01.2025 13:39

    Набор инструментов, необходимых для создания языка C, обычно называют toolchain.

    Ужасно звучит

    Под виндой с удовольствием использовал https://ru.m.wikipedia.org/wiki/LCC