Приветствую!

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

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

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

Блок отвечающий за C++

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

Начало

С чего можно начать?
Начало это описание языка.

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

Что такое язык программирования системного уровня (или низкоуровневый)?

И здесь мы сталкиваемся с первой значимой путаницей между определениями и их интерпретацией. Дело в том что на самом деле язык программирования C является языком высокого уровня, а не низкого как многие его считают.
Почему же так? На самом деле потому что любой язык программирования является автоматически языком высокого уровня, так как сильно абстрагирует нас от вещей которые происходит на самом деле под капотом.
Языком низкого уровня по сути является только язык ассемблера (причем именно язык так как сам ассемблер это лишь сборщик того что написано на языке под него в непосредственно машинный код).
Правильное именование язык системного уровня (так чаще всего например сейчас называют Rust и вот это уже ближе к правде) даст вам больше понимания вещам в самом языке как с более правильным подходом.
А вот язык системного уровня или системно-ориентированный это по сути язык больше подходящий для написания системных утилит и библиотек, системного уровня ПО, драйверов, программного обеспечения для встраиваемых систем и микроконтроллеров.
Для всего остального больше подойдут другие технологии (языки программирования)

Итог: язык который предназначен для создания ПО системного уровня (типо драйверов и системных утилит)

Что такое язык программирования общего назначения?

Это язык который предназначен для создания любого рода программ, который может быть использован под любые задачи. Хотя здесь правильно идти от обратного, что по сути все то что не имеет определенной области применения чаще всего рассчитано на множество задач одновременно.
Также стоит понимать что язык программирования общего назначения это необязательно технология на которой можно создавать все что угодно. Ибо Python тоже язык программирования общего назначения, но написать на нем драйвер или программное обеспечение системного уровня практически невозможно.

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

Что такое язык программирования компилируемого типа?

Тут вроде просто но не совсем. Компиляция это процесс перевода текста языка программирования в машинный код, который может быть исполнен машиной (не всегда процессора, в принципе у практически любой железки есть свой внутренний машинный код как например у видеокарт). То есть данная программа будет полностью привязана к конкретной архитектуре к которой программа была скомпилирована (собрана) и врятли сможет запуститься на другой OC или железе (хотя если они будут поддерживать обратную совместимость или просто совместимость как например у процессоров МЦСТ - Эльбрус, которые вполне могут запускать код для x86 процессора, хотя у данного процессора другая архитектура отличная от x86)

Итог: компиляция процесс где одна программа - компилятор, занимается переводом текста на определенном языке программирования в машинный код или его аналоги (перевод в байт-код для дальнейшего исполнения виртуальной машиной (интерпретатором) часто также называется компиляцией)

Что такое язык программирования со статической, слабой типизацией?

Статическая типизация требует явного (или выведенного компилятором) типа данных на этапе компиляции, чтобы компилятор заранее знал сколько под каждую переменную выделять памяти в оперативной памяти.
Слабая же типизация характерна тем что типы данных могут быть взаимозаменяемы прямым приведением типов.
То есть когда программист захочет явно привести (скастить) тип данных char* в тип данных int* язык никак этому не помешает хотя конечно если в типе char лежала строка и там были символы по 1 байту в длину тогда приведением такого типа данных в int* может явно повлечь множество проблем.
Здесь я подробно описывать это сейчас не буду так как тема тянет на отдельную статью с подробными объяснениями и примерами прямо с кодом, поэтому ограничусь лишь описанием

Итог: Слабая статическая типизация, значит что переменные заранее определены (на этапе компиляции известны компилятору и не могут менять свой тип в ходе выполнения программы. А слабая значит, что компилятор полагается на вас как на разработчика что все неявные и явные приведения типов будут корректными и программа не упадет в Runtime (в момент запуска). И именно поэтому приведение строки к массиву чисел не вызовет у компилятора лишних вопросов если вы явно это укажете, он просто это выполнит без лишних вопросов, хотя это и потенциальный источник огромного количества ошибок.

Что такое язык программирования использующий процедурный стиль создания программ?

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

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

Что такое язык программирования поддерживающий метапрограммирование с помощью встроенного в язык препроцессора?

Это также почти нигде (я вообще нигде не нашел но допускаю что какой-либо сайт со статьей давно канувшей в лету имеет правильное определение) не используемая характеристика хотя именно это очень важная функциональность языка буквально используемая в любой программе, начиная от глобальных констант (#define WELCOME_MESSAGE "Hello, world!") и заканчивая своими шаблонами прямо как в C++ только лишь с возможностями препроцессора!
И на самом деле один лишь препроцессор предоставляет множество возможностей а также гибкости при разработке своего ПО. А если глобально препроцессор вообще по сути является своим собственным языком программирования со своими возможностями и ограничениями (но это лично мой взгляд да и это слишком утрировано звучит, хотя возможно и все таки и не так далеко от истины).

Так что же такое метапрограммирование - это возможность программировать не только саму программу (её создавая или изменяя) но и программировать сам язык динамически его видоизменяя (думаю сумбурно объяснил, но как получилось).
То есть мы явно задаем поведение программы через возможности данные самим языком, а с помощью препроцессора мы можем генерировать код на самом языке C то есть как бы немного программировать сам язык немного расширяя его возможности и когда мы с помощью кода на препроцессоре генерируем код на языке C который затем переводится в машинный код и исполняется как обычная программа.
Причем по сути препроцессора вообще не существует именно в контексте программы так как он будет полностью удален на этапе предобработки текста вашей программы с заменой макро-констант, раскрытием макросов и всякого такого прочего.

Итог: метапрограммирование - это когда мы программируем сам язык программирования (не меняем его поведение, а лишь генерируем на фоне формальных семантик, новые инструкции в языке и это делается автоматически по определенным правилам)

Кратко про препроцессор

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

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

Заключение

Что нужно понимать про сам язык. Сам язык был разработан Денисом Ритчи в 1972 году, а затем популяризирован благодаря книге в соавторстве с Брайаном Керниганом (второй к самому языку никакого отношения не имеет в том числе и по его собственному признанию он лишь в соавторстве с Ритчи выпустили книгу которая лишь придала еще больший импульс C как языку программирования)

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

Поэтому данный язык стоит использовать либо для:

  • Изучения программирования (системно-ориентированного) если вам это требуется, например, по работе или вам просто интересно как устроен компьютер.

  • Написания системно-зависимого или системно-ориентированного ПО (микроконтроллеры, драйверы операционных систем и прочее).

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

В остальных случаях лучше выбрать либо более специфичные под конкретную область инструменты: Python, PHP, C#, Java, либо если вам хочется просто научиться программировать начать с Python/JavaScript - так будет просто проще.

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

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


  1. CatAssa
    16.07.2025 20:26

    Сидела женщина, скучала - (с).


  1. ropblha
    16.07.2025 20:26

    Боюсь фраза "Habr идеальное место для этого " не совсем верна. Вот пикабу, вконтактик, dtf - да. А на хабре могут очень много накидать в панамку за пространные описания на вольные темы. Тут все таки любят серьезно проработанные статьи или посты.


    1. tbl
      16.07.2025 20:26

      автору уже накидали в панамку за предыдущий туториал по установке python


  1. GBR-613
    16.07.2025 20:26

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

    Кстати, неужели на Руси перевелись текстовые редакторы, которые правописание проверяют? Нет, например, слова "врятли", есть выражение "вряд ли".


  1. ABowser
    16.07.2025 20:26

    Некоторым "путь в АйТи" со школьного курса русского языка и литературы надо начинать. Класса с пятого примерно.


    1. SIISII
      16.07.2025 20:26

      Только хотел написать, что перед изучением C стоило бы изучить русский язык хотя б на твёрдую 4 :)


  1. pmikhalovsky
    16.07.2025 20:26

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


    1. randomsimplenumber
      16.07.2025 20:26

      hello world пишется 1 раз. Дальше уже надо начинать понимать, что это и зачем оно.


      1. Abstraction
        16.07.2025 20:26

        Hello world пишется столько раз, сколько надо проверить что система сборки как таковая не объявила забастовку и не сошла с ума. Завидую людям у которых это число раз равно 1.


        1. randomsimplenumber
          16.07.2025 20:26

          система сборки как таковая не объявила забастовку и не сошла с ума

          С чего бы вдруг такое?


          1. Abstraction
            16.07.2025 20:26

            А я знаю? Сидишь себе, никого не трогаешь, тут бац, какой-нибудь find_package(ZLIB REQUIRED) говорит "пошёл нахрен". Или случается The CXX compiler identification is unknown.
            Или просто настраиваешь какой-нибудь инструмент в первый раз, типа "хочу кросс-компилированный под ARM Go, использующий вот этот C-код".

            И во всех этих случаях начинаешь с самого простого: пусть у меня соберётся программа, которая использует все релевантные технологии самым примитивным образом из возможных - напишет в консоль "привет", нарисует чайник, мигнёт одним светодиодом. В этом смысл Hello, world-программ: доказать, что у целевой системы можно вызвать какое-то поведение перед тем как пытаться вызывать сложное.


            1. randomsimplenumber
              16.07.2025 20:26

              И во всех этих случаях начинаешь с самого простого

              Начинать имеет смысл 1 раз. Гонять HelloWorld по кругу - зачем?

              The CXX compiler identification is unknown

              тут никакой HelloWorld не поможет, это чудеса cmake. сжечь build directory.


    1. APh
      16.07.2025 20:26

      Нет. Иначе возможно искажение сознания, когда от чего-то специфического уже будет трудно перейти к базе.

      Не, ну, в Скрэтче можно порисовать, можно.


  1. VladimirFarshatov
    16.07.2025 20:26

    Статья явно переписана с чата гопоты, но соглашусь, по своему опыту обучения сына робототехнике. Путь самурая начинается с Си. И как сетовал и не один препод ВУЗов на соревнованиях: "приходят с питоном, и дать им алгоритмическое программирование на С,С++, Джава увы сложнее на порядок". 13 медалек у сына, в т.ч. третье место по Росси.. проверено опытом.


    1. MountainGoat
      16.07.2025 20:26

      Кажется в этом случае преподаватель ничего кроме С не знает и знать не хочет.


      1. VladimirFarshatov
        16.07.2025 20:26

        Нет, проблема не в этом. Тех кого знал тогда - крутые мамонты, много полезного сделавшие в ИТ. Проблема в ином: самурай вошедший в ИТ с визуальщины и питона, не понимает подкапотные проблемы и ему крайне сложно объяснить концепцию указателя в правильном понимании. Они по большей части так и остаются питонистами "по жизни". Если что, я про детишек в возрасте 8-12 лет, которые входят в ИТ через робототехнику, типа "это просто". Там статистика у преподов была очень впечатляющей.

        Как пример, поездка в ВУЗ на тренировку. Препод ведет занятие должников и заодно работает с нашими детишками по Ардуино. Вышел, кто-то позвал. Две кучки: слева наши, справа студенты (просто присутствовал, запомнилось). Два студента, сдающих зачет:

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

        -"Так там надо в прерывании, обработать вот эту ногу .. (подходит к студенту) - ну да, вот тут у тебя пропущена обработка.. надо вот так.." В этот момент заходит препод обратно:

        -"Так, может я тут и не нужен, может ты сам у него зачет примешь" :)


        1. includedlibrary
          16.07.2025 20:26

          У меня, например, другой опыт. Я начинал с Visual Basic: формочки, простые игрушки, никаких указателей. Потом был Javascript. Концепцию указателей понял почти сразу, как прочёл первый учебник по Си, сложности определённые были, но это всегда так, когда что-то новое осваиваешь. Не могу сказать, что стал крутым знатоком си, но что-то умею. При этом не уверен, что было бы также весело начинать с голого си, а не с формочек и простого бейсика, потому что изначально решил программированием заниматься, чтобы игры делать. Пока на чистом си дойдёшь хотя бы до отрисовки текстуры, уже желание может отпасть.


  1. randomsimplenumber
    16.07.2025 20:26

    информации вроде много, но она уж слишком сильно не структурирована и не систематизирована

    Что нужно понимать про сам язык. Сам язык был разработан Денисом Ритчи в 1972 году

    Фундаментально! Эта информация просто необходима для новичков, да