По данным отраслевых опросов, в 2025 году 84% разработчиков использовали ИИ-инструменты для написания, отладки и автоматизации кода. А в 2026 году ожидается, что более 80% компаний будут применять генеративный ИИ в разработке своих продуктов. Но у LLM есть ограничения, которые не позволяют им быть одинаково эффективными для всех языков программирования — например, для С++.

Тему ограничений AI в пайплайне «плюсовой» разработки обсудили Андрей Золотых и Илья Казаков из YADRO, Константин Владимиров из Синтакора, Илья Шишков из Сбертеха и Денис Фокин из LRI. Читайте, к каким выводам пришли эксперты, и поделитесь своим мнением в комментариях: есть ли место ИИ-агентам в разработке на «плюсах»?


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

Поэтому случайный фрагмент Python-кода со StackOverflow с высокой вероятностью просто запустится. А «рандомный» пример на C++ может скомпилироваться, а затем повести себя некорректно — из-за управления временем жизни и памятью, скрытого неопределенного поведения, а также из-за зависимости от контекста сборки: стандарт, флаги компилятора, ABI, версии библиотек.

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

Особенности C++, которые мешают использовать LLM в разработке

Разработчик на С++ всегда помнит о двух вещах: сколько живет объект и что обязано его уничтожить. Если объекта уже нет, но остались указатель или ссылка на него, мы получим use‑after‑free и Undefined behavior. ИИ-ассистент при генерации кода такие детали нередко упускает. 

В отличие от языков, где есть механизм Garbage collector, в C++ именно программист решает, когда освобождать память и другие ресурсы. RAII и стандартные обертки — умные указатели, контейнеры и scope-guards — помогают реализовать это в коде, но разработчику все равно важно точно понимать, где начинается и где заканчивается жизнь объекта. Такие детали LLM часто оставляет «без внимания», поэтому может легко пропустить ошибки.

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

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

Риск №1: код не будет соответствовать семантике проекта

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

LLM может написать удобный на вид API, который незаметно для разработчика приведет к множественным копиям больших структур:

void log(std::initializer_list<Big> items);
Big a, b;
log({a, b}); // элементы initializer_list — это const, поэтому Big копируется и move не работает

В коде выше есть проблема: элементы std::initializer_list<T> всегда являются константными (const T), поэтому даже временные объекты не перемещаются.

Модель также не отслеживает владение ресурсами и время жизни объектов, поэтому может «забыть» про std::move или вызвать delete там, где он нерелевантен. 

int x = 10;
int* p = &x;
delete p; //  UB: удаляем не-heap объект

ИИ-ассистенты не знают о принципе работы инвариантов, поэтому, вопреки семантике и архитектуре проекта, могут предложить использовать простой int вместо strongly‑typed enum.

Пример:

// 0 = Down, 1 = Up 
void set_admin_state(int state);
set_admin_state(2); // компилируется, но «третье» состояние, которого не существует

Правильно сделать так:

enum class AdminState : uint8_t { Down, Up };
void set_admin_state(AdminState s);
set_admin_state(AdminState::Up);

Риск №2: код не будет соответствовать шаблонам и конкретному стандарту

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

Или, например, в C++20 появились концепции (concept) для проверки требований к типам. Сгенерированный код может создавать слишком строгие ограничения к типам или, наоборот, создавать слабые concept, которые будут пропускать нежелательные типы.

Слишком слабый concept пропускает лишнее: 

#include <concepts>

template<class T>
concept Multipliable = requires(T a, T b) { a * b; };

auto multiply(Multipliable auto a, Multipliable auto b) { return a * b; }

bool b1 = true;
bool b2 = false;
multiply(b1, b2); // Компилируется! bool преобразуется в int → результат int, а не bool

Для корректной работы шаблонов критичны typename, template, правила поиска имен (lookup) и ADL (argument-dependent lookup). Ошибка в этих тонкостях в сгенерированном коде может привести к неожиданному выбору перегрузки, взрыву инстанцирования (превышению глубины шаблонов) или незаметно изменить смысл уже существующего кода.

Дискуссионные вопросы инженеры YADRO также обсуждают в рассылке для разработчиков на С++. Подписывайтесь, чтобы получить последний выпуск, посвященный проведению код-ревью в разных командах.

Как разделять ответственность с LLM

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

Что касается конкретно C++, LLM не стоит использовать как справочник по стандартам. Ответы ИИ-ассистентов об устройстве памяти, модели исключений, порядке инициализации глобальных объектов, гарантированных оптимизациях компилятора могут быть просто неверны, и проверить их гораздо сложнее, чем факт из документации, особенно когда речь идет про UB, порядок вычислений, атомики и ODR. Если разработчик использует такие ответы, то ответственность не лежит на модели: она не отвечает за свои возможные ошибки или неточности.

ChatGPT can (and will) make mistakes
ChatGPT can (and will) make mistakes

Избегать нельзя интегрировать: как C++-разработчику использовать LLM с пользой  

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

  • Надо правильно относиться к ИИ и обозначить базовые правила при работе с ним: «ИИ‑сгенерированный код — всегда требует ревью» или «нельзя ссылаться на LLM как на аргумент в архитектурных решениях».

  • Можно зайти и с другой стороны. Мы лучше понимаем какие-то вещи, когда пытаемся объяснить их другим, а принцип работы LLM заключается именно в обучении. После объяснения какой-то концепции ИИ-ассистенту, вы поймете ее лучше и — как приятный бонус — получите одобрительное «you’re absolutely right». 

  • LLM также может стать замечательным собеседником. Попробуйте отрефлексировать с ИИ-ассистентом код, если обычная уточка уже не помогает. 


В рамках дискуссии «AI-ассистенты в разработке на C++: панацея или опасный костыль» на System Level Meetup эксперты также обсудили:

  • как QA-инженеры начали делать качественные багфиксы с помощью ИИ,

  • какие есть success story интеграции LLM в разработку на C++, 

  • как относятся к сгенерированному коду в open source и многое другое.

Запись можно посмотреть на Rutube и YouTube

А вы используете ИИ-ассистентов в разработке на С++? Пишите в комментариях, как именно и с какими проблемами сталкиваетесь.

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


  1. sasha_solo
    03.03.2026 08:45

    Хоть С и С++ и родные для языки программирования - ни разу не удалось выпустить прогу совсем без утечек памяти. Всегда где-то да была, как бы тщательно я не старался. Так что это не только проблема ЛЛМов. Не спорю, лучшие прогаммисты пишут совсем без утечек, наверное, но я обычный программист.


    1. lamerok
      03.03.2026 08:45

      Поэтому МЭК 61508 запрещает использовать динамическое выделение памяти.

      Только статика, только создание на стейке


      1. unreal_undead2
        03.03.2026 08:45

        Попробуйте написать с такими ограничениями, скажем, компилятор того же C++ ;)


        1. j4niwzis
          03.03.2026 08:45

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


          1. SIISII
            03.03.2026 08:45

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


            1. unreal_undead2
              03.03.2026 08:45

              Согласен, в принципе возможно (но тогда и сам язык был попроще в плане парсинга, и оптимизации в кодогенерации легковеснее). По крайней мере в Fortran 90 динамическая память появилась, но во всяческих бенчмарках (судя по всему с происхождением из научной среды) и в относительно недавние времена встречался с обработкой гигабайтных массивов на стеке - видимо, на Фортране так писать проще и привычнее )


            1. WASD1
              03.03.2026 08:45

              Пул в виде массива.
              Запрос пула из энтри - возвращает индекс в массиве.

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


              1. SIISII
                03.03.2026 08:45

                В реальной реальности прошлого, учитывая очень небольшой объём доступной оперативной памяти (нередко порядка 30 Кбайт на всё про всё, а то и меньше), таблицы символов и т.д. и т.п. хранили во внешней памяти -- на дисках и/или лентах. Работало всё, конечно, очень медленно -- но работало.


      1. apevzner
        03.03.2026 08:45

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

        Только статика, только создание на стейке

        Я дико извиняюсь, о каком стейке идёт речь? Полагаю, о сочном, аккуратно прожаренном стейке из мраморной говядины? :)


        1. lamerok
          03.03.2026 08:45

          :) Полагаю, я был сильно голодный.


      1. boldape
        03.03.2026 08:45

        Каким образом статическая память защищает от утечек? Давайте так вы сначало определите, что такое утечка, а потом почему запрет малока избавляет от этой проблемы. У меня есть большие подозрения, что причина запрета на малок совсем не утечки.


        1. kale
          03.03.2026 08:45

          Вы меня озадачили. А можно пример именно утечек памяти в коде без кучи?


          1. boldape
            03.03.2026 08:45

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


            1. unreal_undead2
              03.03.2026 08:45

              Дальше используем этот массив для нарезки из него каких то обьектов ну например как пул.

              Тогда этот массив - по сути та же самая куча, просто не системная, а реализованная вручную.


              1. boldape
                03.03.2026 08:45

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

                Неиспользование кучи != запрет на переисеользование памяти под разные обьекты при условии соблюдения времен жизни.


                1. unreal_undead2
                  03.03.2026 08:45

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

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


                  1. boldape
                    03.03.2026 08:45

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


                    1. unreal_undead2
                      03.03.2026 08:45

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


    1. apevzner
      03.03.2026 08:45

      Приучите себя к дисциплине:

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

      2. Очень желательно выделение/освобождение сделать симметричным. Следует избегать развесистой логики, которая может в каких-то своих ветвях что-то освободить, а в каких-то нет

      3. Вопрос о владении объектом должен быть всегда очень понятным и прозрачным


      1. SIISII
        03.03.2026 08:45

        Редко, но бывает, что нельзя сразу написать освобождающий код. В таком случае лично я сразу же пишу TODO в точке выделения про то, что надо слепить освобождение -- ну и время от времени проверяю, какие TODO у меня имеются. Помогает от склероза :)


        1. apevzner
          03.03.2026 08:45

          Тоже вполне себе вариант


      1. S_gray
        03.03.2026 08:45

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


    1. Kelbon
      03.03.2026 08:45

      Включите асан


  1. vlsnake
    03.03.2026 08:45

    C++ настолько сложный, что даже LLM "стреляет себе в ногу".


    1. Qwest_Prozto
      03.03.2026 08:45

      Уважающий себя ЯП должен позволять программисту делать все что угодно, включая самоликвидацию /s


  1. unreal_undead2
    03.03.2026 08:45

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

    Для примера попросил Gemini написать проверку "строка X начинается с подстроки Y" - оно выдало разные варианты с указанием, в каких стандартах появились соответствующие фичи. В сложных случаях может не сработать, но с категоричным "не знает" не соглашусь.


    1. j4niwzis
      03.03.2026 08:45

      Про deducting this ллмки практически не знают, хотя с его выхода уже прошло достаточно времени. Но и у компиляторов поддержка не идеальная. К примеру, следующий код уводит gcc 15.2 в сегфолт:

      struct foo {
        int i;
        void bar() {
          [this](this auto){i;};
        }
      };

      А ллмки мне прямо откровенно галлюцинировали. Говорили, что у него синтаксис [this auto] {}, а не [](this auto){}. Или просто говорили, что такой код неправильный.

      А код писало с использованием CRTP, когда там вполне можно было бы заменить и на deducting this.


      1. unreal_undead2
        03.03.2026 08:45

        Попробовал спросить - выдаёт в качестве примера

        auto factorial = [](this auto self, int n) -> int {
            if (n <= 1) return 1;
            return n * self(n - 1); // self refers to the lambda instance
        };

        вроде компилируется )


        1. j4niwzis
          03.03.2026 08:45

          А попросите реализовать обёртку вокруг такой структуры:

          struct generator {
            int state = 0;
            constexpr std::optional<int> next() {
              switch(state) {
                case 0:
                  state = 1;
                  return 5;
                case 1:
                  state = 2;
                  return 42;
                case 2:
                  return std::nullopt;
              }
              return std::nullopt;
            }
          };

          Которая бы через наследование добавляла интерфейс для std::ranges как input_range от T (тут int), что работало бы для разных генераторов, с разными типами значений, для C++23, без упоминания конкретного паттерна.

          Что-то мне кажется, что ИИ в лучшем случае скажет про CRTP, а про deducting this забудет. Поправьте в случае моей неправоты.


          1. unreal_undead2
            03.03.2026 08:45

            Попробуйте сами, gemini.google.com для всех бесплатный ) Мне после намёка "use c++ 23" написало, что можно сделать элегантно с deducing this и что то выдало, но проверять лениво.


            1. j4niwzis
              03.03.2026 08:45

              Без впн не грузит, а с моего впн туда не пускает. Так что увы.

              А если с самого начала сказать про C++23, а не просить улучшить потом?

              Бесплатный ChatGPT про CRTP упомянул, а про Deducting This забыл


              1. unreal_undead2
                03.03.2026 08:45

                Сразу не вышло. После намёка выдаёт такое

                template<typename G>
                struct generator_wrapper : std::ranges::view_interface<generator_wrapper<G>> {
                    G gen;
                    std::optional<int> current;
                
                    struct iterator {
                        generator_wrapper* parent;
                
                        // Deducing this: 'self' identifies the iterator instance
                        auto& operator++(this auto& self) {
                            self.parent->current = self.parent->gen.next();
                            return self;
                        }
                
                        void operator++(int) { ++*this; }
                
                        // Deducing this: handles both const and non-const iterators
                        auto operator*(this const auto& self) {
                            return *self.parent->current;
                        }
                
                        bool operator==(std::default_sentinel_t) const {
                            return !parent->current.has_value();
                        }
                    };
                
                    // Deducing this: determines if begin() is called on an rvalue or lvalue
                    auto begin(this auto& self) {
                        self.current = self.gen.next(); 
                        return iterator{&self};
                    }
                
                    auto end() const { return std::default_sentinel; }
                };


                1. j4niwzis
                  03.03.2026 08:45

                  Так и даже после намёка выдал бред. Людям было бы очевидно, что обёртка не в прямом смысле, а как расширение через наследование, что достаточно часто используется с CRTP и deducting this.


      1. domix32
        03.03.2026 08:45

        Про deducting this ллмки практически не знают,

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


        1. j4niwzis
          03.03.2026 08:45

          Статьи про это есть, разработчикам конкретного проекта про это можно рассказать без проблем.

          Вот в этом и разница человека и ллм. Человек может прочитать статью и сразу везде видеть места, где это можно использовать, где улучшить. А ллм так не может, если в данные обучения закинуть статью просто. Лишь дублировать её части при каждом запросе, что создаёт проблемы.

          Что уж говорить про мои пет проекты, где я использую некоторые возможности из C++26 ))


          1. domix32
            03.03.2026 08:45

            Статьи про это есть, разработчикам конкретного проекта про это можно рассказать без проблем.

            вы забываете про значение L в LLM. то бишь large. Десяток статей на три с половиной землекопа в эту категорию определённо не тянет. Аналогичная проблема с кодом. Популярный open source не очень-то стремится включать новые стандарты, особенно когда у тех ещё и полноценной имплементации нет. С учётом этой фрагментации ещё и обратную совместимость придётся держать, по-хорошему. ну или под дырки конкретных компиляторов какие-нибудь костыли-полифилы добавлять.

            и сразу везде видеть места, где это можно использовать

            Звучит как "если у вас есть молоток, то всё становится похожим на гвоздь". Ну, и кажется давно не новость, что LLM не умеют в действительно новые знания, а штуки типа copilot учатся на тоннах гуано в том числе.


            1. TimurZhoraev
              03.03.2026 08:45

              Именно! Так зачем теперь нужно дополнять ценные гигаватт-часы на дополнительные новомодные триллионы токенов, когда банальный С вытягивает 100% проблем. Да, человеку неудобно, а кто сказал что там что-то нужно читать и знать когда есть 100500 различных парсеров, тестов, санитайзеров, которые генерируются или обвязка для которых налету llm-кой. Вплоть до питона, который анализирует С, делает заголовки и код С, заготавливает метрику, отправляет в LLM, делает автоматом рефакторинг, с файлами -конфигами больше уже нет необходимости возиться, да и версия кода не есть версия текста программы по большому счёту. Это про комментарии и их хранилище с нагенерённой документацией и summary от LLM.


  1. codecity
    03.03.2026 08:45

    Не уверен что все так. Дело вот в чем. Человеку сложно запоминать информацию. Даже 10 Мб. документации (книгу) слово в слово запомнить и вспомнить в нужный момент - займет несколько лет (лет 5). Т.е. все типы UB, все тонкости стандартов разных версий - очень и очень сложно запомнить кожаному, но совсем просто LLM-ке. То что у человека займет годы тяжкого труда - у LLM-ки минуты.

    По этому может оказаться наоборот - человеку очень сложно, т.к. нужно слишком много держать в голове и ничего не упустить. А LLM-ке легко.


    1. zum
      03.03.2026 08:45

      Всё так, но лучше лишний раз "прочитать мануал".


      1. TimurZhoraev
        03.03.2026 08:45

        Позвонить в сервис, попросить чтобы дали задание написать мануал для LLM с годной векторной базой данных. Старые мануалы скорее переживают суровый закат, всевозможные языки разметок чтобы быть покрасивее, все эти md, man, hlp, да ещё и не векторные и не структурированные рисунки. Машинно-ориентированный мануал это фактически генератор знаний по запросу. Линейные книги и html-ки это уже прошлый век.


        1. zum
          03.03.2026 08:45

          Что-то слишком много сущностей появляется в цепочке между "информацией" и её "потребителем", вам так не кажется?


    1. Pubert
      03.03.2026 08:45

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

      Когда человек читает, он ассоциирует текст со своим опытом. У LLM, увы, нет опыта. В этом принципиальная разница. Поэтому человеку так сложно запомнить что-то большое. И поэтому человек может разобраться в чем-то по-настоящему глубоко


      1. codecity
        03.03.2026 08:45

        LLM читает быстро, но поверхностно

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

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


  1. apevzner
    03.03.2026 08:45

    А какой из перечисленных рисков не относится к коду, написанному людьми?

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

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


    1. j4niwzis
      03.03.2026 08:45

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


      1. apevzner
        03.03.2026 08:45

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


        1. Qwest_Prozto
          03.03.2026 08:45

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


          1. TimurZhoraev
            03.03.2026 08:45

            Проблема скорее в формализации качества. Новая фишка должна либо быть более понятной, более безопасной, уменьшить время на её обучение, на разработку, величину кода, его эффективность. Все языки как раз состязались именно чтобы быть приятными человеку, машине всё равно что там написано. Когда же пришла LLM это соревнование потеряло смысл и проще взять совершенно базовые вещи языков высокого уровня которые становятся просто ассемблером для LLM. И чем лаконичнее язык в плане грамматики и синтаксиса тем лучше. В конечном итоге это С (с диалектами CUDA, шейдеры, OpenCL, OpenMP...), Питон, Бэйсик, Фортран, ЛИСП, Verilog ну и ещё что-то из разряда Maxima, Maple, Mathematica. Оркестрируя этим делом можно составить приложение любой сложности.


  1. domix32
    03.03.2026 08:45

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

    Не знаю какой LLM вы использовали, но deep seek вполне знает о многочисленных стандартах, но вот писать с ограничениями по фишкам конкретных стандартов не умеет - пока не скормишь соотвествующую страничку с cppreference оно тебе и не скажет, почему его же код не компилируется, которому просто нужно или компилятор поновее или флагов прокинуть. Аналогичным образом не знает ограничений по компиляторам. Поэтому для нормальной работы там обычно нужно дополнительно готовить ему контекст, что проблемно. Chat GPT грешил тем, что он не исправлял ошибки в коде, хотя говорил "да да точно вот тут ошибка, вот исправленный код". За всякие Claude и иже с ними не расскажу, не пробовал. Код на питоне генерировало не слишком хорошо, на JS видимо та же проблема.


  1. progmanth
    03.03.2026 08:45

    
    int x = 10;
    int* p = &x;
    delete p; //  UB: удаляем не-heap объект

    ответ ChatGPT на копипаст кода даже без вопроса:

    Это жёсткое UB (undefined behavior).

    Почему:

    • xне в куче, это автоматическая переменная на стеке.

    • delete можно делать только для указателей, полученных через new (и совместимых форм: new[]delete[]).

    Что будет на практике: иногда “прокатит”, иногда упадёт, иногда тихо сломает аллокатор/кучу и выстрелит позже.

    void log(std::initializer_list<Big> items);
    Big a, b;
    log({a, b}); 

    ответ ChatGPT:

    log({a, b}); почти наверняка делает копии a и b (или пытается), потому что std::initializer_list<T> — это view на массив временных объектов T const, созданный компилятором из {...}.

    Как избежать копий

    Вариант A: принимать initializer_list<std::reference_wrapper<const Big>>

    Тогда {a, b} будет списком ссылок, без копирования Big:

    #include <initializer_list>#include <functional>void log(std::initializer_list<std::reference_wrapper<const Big>> items);Big a, b;log({std::cref(a), std::cref(b)});

    Вариант B: variadic (и логировать “в лоб”)

    template<class... Ts>void log(const Ts&... xs);log(a, b);

    Вариант C: span на массив/вектор (если данные уже где-то живут)

    #include <span>void log(std::span<const Big> items);

    Но log({a,b}) уже не подойдёт напрямую без создания массива; зато std::array<Big,2>{a,b} снова копии. Для ссылок делай std::array<std::reference_wrapper<const Big>,2>.

    // 0 = Down, 1 = Up 
    void set_admin_state(int state);
    set_admin_state(2);

    Если контракт такой: только 0 (Down) или 1 (Up), то set_admin_state(2) — это ошибка входных данных, и вопрос в том, как ты хочешь, чтобы система себя вела (assert, исключение, игнор, clamp, лог + default). В проде обычно делают валидацию + безопасный отказ.

    Лучше: enum class, чтобы “2” стало сложнее написать

    enum class AdminState : uint8_t { Down = 0, Up = 1 };

    void set_admin_state(AdminState state)
    {
    switch (state) {
    case AdminState::Down: /*...*/ break;
    case AdminState::Up: /*...*/ break;
    }
    }

    // usage
    set_admin_state(AdminState::Up);

    Итог: set_admin_state(2); — это invalid argument. Если сейчас функция молча принимает 2 и делает “что-то”, это баг дизайна API: лучше либо типизировать, либо явно валидировать и возвращать ошибку/логировать.

    #include <concepts>
    
    template<class T>
    concept Multipliable = requires(T a, T b) { a * b; };
    
    auto multiply(Multipliable auto a, Multipliable auto b) { return a * b; }
    
    bool b1 = true;
    bool b2 = false;
    multiply(b1, b2); // Компилируется! bool преобразуется в int → результат int, а не bool

    ответ ChatGPT:

    Это скомпилируется (в обычном C++20), потому что:

    • bool удовлетворяет твоему Multipliable: выражение a * b валидно.

    • При bool bool оба bool проходят целочисленное продвижение (false -> 0, true -> 1) и операция умножения делается как int int.

    • Поэтому multiply(b1, b2) вернёт int, а не bool.

      Как запретить bool

      Самый простой фильтр в концепте:

    • #include <concepts>#include <type_traits>

      template<class T>
      concept Multipliable =
      requires(T a, T b) { a * b; } &&
      (!std::same_as<std::remove_cvref_t<T>, bool>);

      auto multiply(Multipliable auto a, Multipliable auto b) { return a * b; }

    Chat GPT 5.2 thinking

    ну вот как то так.


    1. TimurZhoraev
      03.03.2026 08:45

      Чем больше синтаксического сахара - тем больше можно получить случайной соли.


      1. progmanth
        03.03.2026 08:45

        Дай дураку xyz стеклянный - он и xyz разобьет и руки порежет.

        если не умеешь пользоваться инструментом - не мучай то место где спина теряет свое гордое название


        1. TimurZhoraev
          03.03.2026 08:45

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


    1. Pubert
      03.03.2026 08:45

      А теперь попросите исправить код)))


  1. mozg37
    03.03.2026 08:45

    Ну бред же. Лично юзаю дипсик как толпу тестировщиков. Да, я тестирую части проектов fpga посредством небольших прог на с++. Главное - правильный промт.


    1. Qwest_Prozto
      03.03.2026 08:45

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


      1. mozg37
        03.03.2026 08:45

        Несколько тредов, прием/передача udp, чек принятого, логи в csv. Это тривиально?


        1. dephonica
          03.03.2026 08:45

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

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

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


          1. TimurZhoraev
            03.03.2026 08:45

            так нужно генерировать неправильный код на очень хорошей платформе, который будет генерировать правильный код на плохой. Иными словами кто мешает сгенерировать питон который будет следить за дефайнами, стимулусами, тестбенчами и прочими do-файлами со времён tcl tk, и иже с ними имеющие кодовую базу в тератокены. То есть он будет делать C-вставки в проект, включая парсинг самих C-файлов проекта тем же cparcer. В этом и заключается сила простых языков с точки зрения промптинга и анализа что для них есть превосходные инструменты для обратных связей - вытаскивать структуры, определения, функции итд и встраивать то что необходимо, включая тесты. Изолированная единица с параметром - и настраивай как угодно, остальные имеют те же питон модели поведения, не обязательно всё возлагать на один язык. Достаточно промоделировать и использовать сгенерированный код. В ту же копилку и DMA с режимами работы, потокобезопасные очереди, всевозможные контроллеры прерываний с приоритетами итд


            1. dephonica
              03.03.2026 08:45

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

              LLM не может предложить алгоритм (архитектуру, если хотите) в озвученных ей технических ограничениях. Предлагает аргументированные, но некорректные компиляции решений, о которых она где-то слышала: здесь лучше PLL, но если хотите, то PID регулятор. В качестве базиса будем использовать PTP, хотя аппаратной поддержки его нет и он не заведётся на устройстве. Входной сигнал для PLL/PID будем брать из усреднённых скоростей хода таймеров и тут же пытается скрестить этот сигнал со средним наполнением входящего буфера данных.

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

              UPD: Из других ваших ответов кажется понял, что вы говорите о генерации индивидуального для проекта фреймворка для моделирования/тестирования с помощью LLM. Это может закрыть какие-то варианты потенциальных ошибок (примерно как статический анализатор), но почти на 100% бесполезно в сильно асинхронном производительном коде, где как раз проблемы с отладкой. Сильно асинхронный здесь - это когда не только несколько (десятков) разнородных потоков на один процесс, но и несколько процессов с сетевым взаимодействием между собой как локально, так и на разных устройствах/в разных сетях. Такое в принципе не моделируется в контексте поиска конфликтов в коде, кроме каких-то тривиальных случаев.

              И здесь как раз есть те самые асинхронные аренда/освобождение памяти, которые LLM не могут отследить ни своим пристальным взглядом, ни внешним специально сгенерированным инструментарием, так как код лаконичен, идеален и работает 99.999% времени так, как предполагается. Но под другой ОС он работает правильно 99.8% времени, так как поведение реализации сетевых сокетов в ядре немного отличается и есть редкие случаи потерь пакетов, которые вызывают не ту реакцию, которую ожидает код. Такое отлаживается только традиционными методами, чаще всего вообще трейсами/логами.

              Их можно скормить LLM предварительно пропарсив чтобы уменьшить объём, но это уже заход на второй круг и здесь полно своих проблем.


  1. yaroslavp
    03.03.2026 08:45

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


    1. TimurZhoraev
      03.03.2026 08:45

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


  1. TimurZhoraev
    03.03.2026 08:45

    Для решения проблемы с динамической памятью можно использовать штатный санитайзер gcc или просить модель сделать smart pointer даже на простом С. Вообще говоря этот язык переживёт с LLM второе рождение, так как все новомодные фишки которые появились за последнее десятилетие в языках и призванные синтаксисом улучшить читабельность для человека уйдут на второй план, так как контроль за надёжностью и управлением памяти как раз может взять на себя модель, мало того, сугубо компилируемые языки разрослись до уровня интерпретаторов, для нововведений нет кодовой базы для глубокого обучения. Более того, стиль написания скриптов будет примерно такой, как новый язык. Известно, что в моделях есть "температура", а значит, и "энтропия" (cross-entropy). Поэтому промпт-ТЗ должно предельно иметь её высокое значение относительно алгоритма без лишней составляющей. Пример для простого С, используемого как ассемблер для промпта. Сделаем цифровой фильтр с динамической памятью.


    Практикум вайб-курса цифровой обработки сигналов в одном Хабр-комментарии. Промптим код без демонов Максвелла с хорошей энтропией:

    Язык С мэйн иметь флоат два массива cx и x длина nx два массива cy и y длина ny объединить одна структура глобальная поля структура сделать маллок область видимости выход фри. Инициализация x и y ноль. Мэйн внутри цикл отсчёты внутри аккумулятор ноль первый элемент x равно константа цикл по элемент массив cx и x умножение с накоплением цикл по элемент массив cy и y аккумулятор минус умножение с накоплением. Сохранить аккумулятор первый элемент массив y. Сдвинуть элемент массив x и массив y один позиция вправо, инкремент индекса только использовать временная переменная тэ1 тэ2 обмен.

    Код-скелет
    #include <stdio.h>
    #include <stdlib.h>
    
    // Константы для примера
    #define NX 3
    #define NY 3
    #define STEPS 10
    #define CONST_VAL 1.0f
    
    // --- Глобальная структура ---
    typedef struct {
        float *cx;
        float *x;
        int nx;
        
        float *cy;
        float *y;
        int ny;
    } FilterData;
    
    FilterData g_data; // Глобальный экземпляр
    
    int main() {
        // --- Выделение памяти (malloc) ---
        g_data.nx = NX;
        g_data.ny = NY;
        
        g_data.cx = (float*)malloc(g_data.nx * sizeof(float));
        g_data.x  = (float*)malloc(g_data.nx * sizeof(float));
        
        g_data.cy = (float*)malloc(g_data.ny * sizeof(float));
        g_data.y  = (float*)malloc(g_data.ny * sizeof(float));
    
        // --- Инициализация ---
        // Коэффициенты (для примера)
        for(int i = 0; i < g_data.nx; i++) { g_data.cx[i] = 0.5f; g_data.x[i] = 0.0f; }
        for(int i = 0; i < g_data.ny; i++) { g_data.cy[i] = 0.2f; g_data.y[i] = 0.0f; }
    
        printf("Starting processing loop...\n");
    
        // --- Основной цикл (Отсчеты) ---
        for (int t = 0; t < STEPS; t++) {
            float acc = 0.0f;
            
            // Первый элемент x равен константе
            g_data.x[0] = CONST_VAL;
    
            // Цикл по cx и x: умножение с накоплением
            for (int i = 0; i < g_data.nx; i++) {
                acc += g_data.cx[i] * g_data.x[i];
            }
    
            // Цикл по cy и y: аккумулятор минус умножение с накоплением
            // (Обычно пропускают y[0] в обратной связи, но делаем строго по описанию)
            for (int i = 0; i < g_data.ny; i++) {
                acc -= g_data.cy[i] * g_data.y[i];
            }
    
            // Сохранить аккумулятор в первый элемент y
            g_data.y[0] = acc;
    
            printf("Step %d: Result Y[0] = %.4f\n", t, g_data.y[0]);
    
            // --- Сдвиг элементов вправо с использованием временных переменных ---
            // Сдвиг x
            float te1 = g_data.x[0];
            for (int i = 1; i < g_data.nx; i++) {
                float te2 = g_data.x[i];
                g_data.x[i] = te1;
                te1 = te2;
            }
    
            // Сдвиг y
            te1 = g_data.y[0];
            for (int i = 1; i < g_data.ny; i++) {
                float te2 = g_data.y[i];
                g_data.y[i] = te1;
                te1 = te2;
            }
        }
    
        // --- Освобождение памяти (free) ---
        free(g_data.cx);
        free(g_data.x);
        free(g_data.cy);
        free(g_data.y);
    
        return 0;
    }
    

    Хотеть коэффициент cx массив как единица делить количество. Нужно первый коэффициент cy равно ноль. Ещё два коэффициент cy посчитать полюс устойчивость единичная окружность комплексная плоскость хорошо остальные ноль.

    Догадайся что это IIR
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    #define NX 5
    #define NY 5
    #define STEPS 15
    #define CONST_VAL 1.0f
    
    typedef struct {
        float *cx;
        float *x;
        int nx;
        
        float *cy;
        float *y;
        int ny;
    } FilterData;
    
    FilterData g_data;
    
    int main() {
        // --- Выделение памяти ---
        g_data.nx = NX;
        g_data.ny = NY;
        g_data.cx = (float*)malloc(g_data.nx * sizeof(float));
        g_data.x  = (float*)malloc(g_data.nx * sizeof(float));
        g_data.cy = (float*)malloc(g_data.ny * sizeof(float));
        g_data.y  = (float*)malloc(g_data.ny * sizeof(float));
    
        // --- Инициализация массивов ---
        for(int i = 0; i < g_data.nx; i++) {
            g_data.cx[i] = 1.0f / (float)g_data.nx; // Коэффициент 1/N
            g_data.x[i] = 0.0f;
        }
    
        // Инициализация cy нулями
        for(int i = 0; i < g_data.ny; i++) {
            g_data.cy[i] = 0.0f;
            g_data.y[i] = 0.0f;
        }
    
        // Расчет коэффициентов cy для устойчивых полюсов
        float r = 0.9f;         // Радиус (внутри единичной окружности)
        float theta = 0.5f;     // Угол (фаза)
        
        g_data.cy[0] = 0.0f;    // Первый коэффициент ноль по условию
        if (g_data.ny > 2) {
            g_data.cy[1] = -2.0f * r * cosf(theta); 
            g_data.cy[2] = r * r;
        }
    
        printf("cx[0] = %.4f, cy[1] = %.4f, cy[2] = %.4f\n", g_data.cx[0], g_data.cy[1], g_data.cy[2]);
        printf("--------------------------------------\n");
    
        // --- Основной цикл ---
        for (int t = 0; t < STEPS; t++) {
            float acc = 0.0f;
            
            g_data.x[0] = CONST_VAL;
    
            // Умножение с накоплением для x
            for (int i = 0; i < g_data.nx; i++) {
                acc += g_data.cx[i] * g_data.x[i];
            }
    
            // Вычитание умножения с накоплением для y
            for (int i = 0; i < g_data.ny; i++) {
                acc -= g_data.cy[i] * g_data.y[i];
            }
    
            g_data.y[0] = acc;
    
            printf("Step %d: y[0] = %.4f\n", t, g_data.y[0]);
    
            // --- Сдвиг x ---
            float te1 = g_data.x[0];
            for (int i = 1; i < g_data.nx; i++) {
                float te2 = g_data.x[i];
                g_data.x[i] = te1;
                te1 = te2;
            }
    
            // --- Сдвиг y ---
            te1 = g_data.y[0];
            for (int i = 1; i < g_data.ny; i++) {
                float te2 = g_data.y[i];
                g_data.y[i] = te1;
                te1 = te2;
            }
        }
    
        // --- Очистка ---
        free(g_data.cx);
        free(g_data.x);
        free(g_data.cy);
        free(g_data.y);
    
        return 0;
    }
    

    Принтф "импульсная характеристика" принтф формат CSV в файл "эксперимент" тэикстэ на дисплей тоже красиво табуляция массив цикл отсчёты.

    ввод-вывод
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    #define NX 5
    #define NY 5
    #define STEPS 20
    
    typedef struct {
        float *cx, *x, *cy, *y;
        int nx, ny;
    } FilterData;
    
    FilterData g_data;
    
    int main() {
        g_data.nx = NX; g_data.ny = NY;
        g_data.cx = (float*)malloc(NX * sizeof(float));
        g_data.x  = (float*)calloc(NX, sizeof(float)); // Инициализация 0
        g_data.cy = (float*)malloc(NY * sizeof(float));
        g_data.y  = (float*)calloc(NY, sizeof(float)); // Инициализация 0
    
        // Настройка коэффициентов
        for(int i = 0; i < NX; i++) g_data.cx[i] = 1.0f / NX;
        for(int i = 0; i < NY; i++) g_data.cy[i] = 0.0f;
        
        float r = 0.95f;      // Полюс внутри окружности
        float theta = 0.4f;   // Угол
        g_data.cy[1] = -2.0f * r * cosf(theta); 
        g_data.cy[2] = r * r;
    
        FILE *f = fopen("experiment.txt", "w");
        if (!f) return 1;
    
        printf("Импульсная характеристика:\n");
        printf("T\tInput\tOutput\n");
        printf("--------------------------\n");
        fprintf(f, "Step,Input,Output\n"); // Заголовок CSV
    
        for (int t = 0; t < STEPS; t++) {
            // Импульс: 1.0 только в t=0, иначе 0.0
            float current_input = (t == 0) ? 1.0f : 0.0f;
            g_data.x[0] = current_input;
    
            float acc = 0.0f;
            for (int i = 0; i < NX; i++) acc += g_data.cx[i] * g_data.x[i];
            for (int i = 0; i < NY; i++) acc -= g_data.cy[i] * g_data.y[i];
    
            g_data.y[0] = acc;
    
            // Вывод: дисплей (табуляция) и файл (CSV)
            printf("%d\t%.2f\t%.4f\n", t, current_input, acc);
            fprintf(f, "%d,%.2f,%.4f\n", t, current_input, acc);
    
            // Сдвиг x
            float te1 = g_data.x[0];
            for (int i = 1; i < NX; i++) {
                float te2 = g_data.x[i];
                g_data.x[i] = te1;
                te1 = te2;
            }
            // Сдвиг y
            te1 = g_data.y[0];
            for (int i = 1; i < NY; i++) {
                float te2 = g_data.y[i];
                g_data.y[i] = te1;
                te1 = te2;
            }
        }
    
        fclose(f);
        free(g_data.cx); free(g_data.x); 
        free(g_data.cy); free(g_data.y);
        return 0;
    }
    

    Ой плохо проверка после маллок массив что индекс цикл не высоко его размер сделать до 20. Но вводи сканф размер массив x,y и cx,cy клавиатура если энтер и энтер то по умолчанию.

    Тест границы массива
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    
    #define MAX_SIZE 20
    #define DEFAULT_NX 5
    #define DEFAULT_NY 5
    #define STEPS 20
    
    typedef struct {
        float *cx, *x, *cy, *y;
        int nx, ny;
    } FilterData;
    
    FilterData g_data;
    
    // Функция для безопасного ввода размера
    int get_size(const char *name, int def_val) {
        char buffer[100];
        printf("Введите размер %s (по умолчанию %d, макс %d): ", name, def_val, MAX_SIZE);
        if (fgets(buffer, sizeof(buffer), stdin) == NULL || buffer[0] == '\n') {
            return def_val;
        }
        int val = atoi(buffer);
        if (val <= 0) return def_val;
        if (val > MAX_SIZE) return MAX_SIZE;
        return val;
    }
    
    int main() {
        // Ввод размеров с клавиатуры
        g_data.nx = get_size("X/CX", DEFAULT_NX);
        g_data.ny = get_size("Y/CY", DEFAULT_NY);
    
        // --- Выделение памяти с проверкой ---
        g_data.cx = (float*)malloc(g_data.nx * sizeof(float));
        g_data.x  = (float*)calloc(g_data.nx, sizeof(float));
        g_data.cy = (float*)malloc(g_data.ny * sizeof(float));
        g_data.y  = (float*)calloc(g_data.ny, sizeof(float));
    
        if (!g_data.cx || !g_data.x || !g_data.cy || !g_data.y) {
            printf("Ошибка: не удалось выделить память!\n");
            return 1;
        }
    
        // --- Инициализация коэффициентов ---
        for(int i = 0; i < g_data.nx; i++) g_data.cx[i] = 1.0f / (float)g_data.nx;
        for(int i = 0; i < g_data.ny; i++) g_data.cy[i] = 0.0f;
        
        // Полюса для устойчивости
        float r = 0.95f; 
        float theta = 0.4f;
        if (g_data.ny > 2) {
            g_data.cy[1] = -2.0f * r * cosf(theta); 
            g_data.cy[2] = r * r;
        }
    
        // --- Работа с файлом и вывод ---
        FILE *f = fopen("experiment.txt", "w");
        if (!f) {
            printf("Ошибка открытия файла!\n");
            return 1;
        }
    
        printf("\nИмпульсная характеристика (NX=%d, NY=%d):\n", g_data.nx, g_data.ny);
        printf("T\tInput\tOutput\n");
        printf("--------------------------\n");
        fprintf(f, "Step,Input,Output\n");
    
        for (int t = 0; t < STEPS; t++) {
            float current_input = (t == 0) ? 1.0f : 0.0f;
            g_data.x[0] = current_input;
    
            float acc = 0.0f;
            // Циклы теперь ограничены введенными размерами
            for (int i = 0; i < g_data.nx; i++) acc += g_data.cx[i] * g_data.x[i];
            for (int i = 1; i < g_data.ny; i++) acc -= g_data.cy[i] * g_data.y[i];
    
            g_data.y[0] = acc;
    
            printf("%d\t%.2f\t%.4f\n", t, current_input, acc);
            fprintf(f, "%d,%.2f,%.4f\n", t, current_input, acc);
    
            // Сдвиг x через временные переменные
            float te1 = g_data.x[0];
            for (int i = 1; i < g_data.nx; i++) {
                float te2 = g_data.x[i];
                g_data.x[i] = te1;
                te1 = te2;
            }
            // Сдвиг y через временные переменные
            te1 = g_data.y[0];
            for (int i = 1; i < g_data.ny; i++) {
                float te2 = g_data.y[i];
                g_data.y[i] = te1;
                te1 = te2;
            }
        }
    
        // --- Завершение ---
        fclose(f);
        free(g_data.cx); free(g_data.x);
        free(g_data.cy); free(g_data.y);
        printf("\nДанные сохранены в experiment.txt. Память очищена.\n");
    
        return 0;
    }
    

    Хотеть ГНУ мэйкфайл компиляция что мой С стандарт проверка не хуже C11 мой архитектура есть 64 бита и процессор x86 а не АРМ иначе ошибка. Называю mymain си мой файл я

    Антилопа мэйкфайл
    # Имя исходного файла и исполняемого файла
    TARGET = mymain
    SRC = mymain.c
    
    # Настройки компилятора
    CC = gcc
    CFLAGS = -std=c11 -Wall -Wextra -pedantic -lm
    
    # Определение архитектуры и разрядности
    ARCH = $(shell uname -m)
    BITS = $(shell getconf LONG_BIT)
    
    # Проверка условий: x86_64 и 64 бита
    all: check_arch $(TARGET)
    
    check_arch:
    	@echo "Проверка системных требований..."
    	@if [ "$(ARCH)" != "x86_64" ]; then \
    		echo "Ошибка: Архитектура $(ARCH) не поддерживается. Нужен x86_64 (не ARM)."; \
    		exit 1; \
    	fi
    	@if [ "$(BITS)" != "64" ]; then \
    		echo "Ошибка: Требуется 64-битная система."; \
    		exit 1; \
    	fi
    	@echo "Система подходит: $(ARCH) $(BITS)-bit. Стандарт C11."
    
    $(TARGET): $(SRC)
    	$(CC) $(CFLAGS) $(SRC) -o $(TARGET)
    
    clean:
    	rm -f $(TARGET) experiment.txt
    
    run: all
    	./$(TARGET)
    

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

    Бэш питон вэнв автомат
    #!/bin/bash
    
    # 1. Проверка наличия Python-скрипта
    if [ ! -f "graph.py" ]; then
        echo "Ошибка: Файл graph.py не найден! Пожалуйста, создайте скрипт."
        exit 1
    fi
    
    # 2. Проверка и создание venv
    VENV_DIR="venv"
    
    if [ ! -d "$VENV_DIR" ]; then
        echo "Виртуальное окружение не найдено. Создаю..."
        python3 -m venv $VENV_DIR
        source $VENV_DIR/bin/activate
        echo "Установка зависимостей (matplotlib)..."
        pip install --upgrade pip
        pip install matplotlib
    else
        echo "Активация существующего окружения..."
        source $VENV_DIR/bin/activate
    fi
    
    # 3. Запуск Python-скрипта
    echo "Запуск визуализации..."
    python3 graph.py
    
    # Деактивация после выхода
    deactivate
    

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

    Питон язык строить CSV
    import matplotlib.pyplot as plt
    import csv
    import os
    
    def draw_graph():
        filename = "experiment.txt"
        
        # Проверка файла
        if not os.path.exists(filename):
            print(f"Ошибка: Создайте сначала {filename} (запустите C программу)")
            return
    
        steps = []
        values = []
    
        # Чтение данных без использования сторонних пакетов (только стандартный csv)
        try:
            with open(filename, "r", encoding="utf-8") as f:
                reader = csv.reader(f)
                next(reader)  # Пропускаем заголовок (Step, Input, Output)
                for row in reader:
                    if len(row) >= 3:
                        steps.append(int(row[0]))   # Время / Номер отсчета
                        values.append(float(row[2])) # Значение (Output)
        except Exception as e:
            print(f"Ошибка чтения CSV: {e}")
            return
    
        # --- Настройка визуализации (Красиво) ---
        plt.figure(figsize=(11, 6))
        
        # Рисуем линии и точки одновременно
        # 'o-' означает точки и сплошную линию
        # color='#2c3e50' - темно-синий/графитовый цвет
        plt.plot(steps, values, 'o-', color='#e74c3c', linewidth=1.5, 
                 markersize=5, markerfacecolor='#2980b9', markeredgecolor='white',
                 label='Импульсный отклик')
    
        # Оформление осей и заголовка
        plt.title("Импульсная характеристика системы", fontsize=14, pad=15)
        plt.xlabel("Номер отсчета (n)", fontsize=11)
        plt.ylabel("Амплитуда сигнала (y)", fontsize=11)
    
        # Сетка
        plt.grid(True, linestyle='--', alpha=0.7)
        
        # Линии осей для наглядности (проходят через 0)
        plt.axhline(0, color='black', linewidth=0.8, alpha=0.5)
        plt.axvline(0, color='black', linewidth=0.8, alpha=0.5)
    
        plt.legend()
        
        # Сохранение и отображение
        print("Сохранение графика в impulse_response.png...")
        plt.savefig("impulse_response.png", dpi=200)
        plt.show()
    
    if __name__ == "__main__":
        draw_graph()
    
    Результат

    Это импульсная характеристика или переходная на функцию Хевисайда - уже вопрос для билета


  1. lhog
    03.03.2026 08:45

    Такое ощущение что статья года 3 лежала в черновике или коллеги делали свои выводы на базе какой-то Алисы-GPT или что там еще в России не запретили.

    Мой экспиренс строго противоположный: GPT-5, модели Антропика генерят отличный C++ код. Открытые китайские модели чуть похуже, но там скорее вопрос не самой лучшей архитектуры и DRY, а не каких-то детских ошибок типа удаления указателя на переменную из стека.


  1. diffnotes-tech
    03.03.2026 08:45

    "случайный фрагмент Python-кода со StackOverflow с высокой вероятностью просто запустится" - ну да, запустится. А потом в три ночи в проде тоже запустится но уже по-своему) Проблема не в том что C++ сложный для AI, а в том что мы привыкли к питону где "запустилось = работает"


  1. max-daniels
    03.03.2026 08:45

    Так надо писать код так, чтобы не приходилось вручную писать delete(смартпоинтеры, например). У меня в проекте никаких утечек нет, хотя почти весь код пишет ИИ. Просто надо прописать правила у ИИ.