Ответы пользователя по тегу Программирование
  • Как получить доступ к полям и методам неизвестного объекта?

    @Mercury13
    Программист на «си с крестами» и не только
    Это значит: объект класса B должен иметь (или не иметь) «товарища» класса C. Иметь не во владении, а по ссылке. А лучше не класса C, а его подмножества, реализованного как интерфейс (я его назвал IC). На A вообще чхаем — его задача собрать B и C в нужном виде, и всё.

    Смотрите шаблон проектирования Dependency Injection.

    class IC {   // interface
    public:
      virtual int getC() = 0;
      virtual ~IC() = default;
    };
    
    class C : public IC {
    public:
      int getC() final { return 42; }
    };
    
    class B {
    public:
      IC* buddy() const { return fBuddy; }
      void setBuddy(IC* aBuddy) { fBuddy = aBuddy; }
      void someJob() const { if (fBuddy) std::cout << fBuddy->getC() << std::endl; }
    private:
      IC* fBuddy = nullptr;
    };
    
    class A {
    public:
      A() { b.setBuddy(&c); }
    private:
      C c;
      B b;
    }

    Специально для тех, кто на Си++: в таком виде A неперемещаем из-за указателя c.fBuddy. Есть много способов исправить это — например, подправить конструктор копирования и операцию «присвоить» класса A.
    Ответ написан
    3 комментария
  • Как вставить значение строки как аргумент функции?

    @Mercury13
    Программист на «си с крестами» и не только
    1. В строковой переменной или строковом буфере собрать тело команды, экранируя параметры, если это нужно.
    2. Если это обёрнутый string, преобразовать в const char* — и пошёл.
    std::string command;
    char data1 = 'F';
    char data2 = 'C';
    command = std::string("color ") + data1 + data2;
    system(command.c_str());
    Ответ написан
    1 комментарий
  • Как писать тест-кейсы к функциям?

    @Mercury13
    Программист на «си с крестами» и не только
    Без того, что она делает, мы никак не можем написать нужные сценарии.

    Например, тестируем функцию abs(x) — чтобы проверить её работоспособность, достаточно подать на вход что-то положительное, что-то отрицательное и ноль.
    Например, abs(2) = 2, abs(−3) = 3, abs(0) = 0.

    Для вашего примера надо проверить B = true и B = false — а остальное шут его знает.
    Ответ написан
    1 комментарий
  • Как востановить бинарное дерево?

    @Mercury13
    Программист на «си с крестами» и не только
    Если одним префиксным/инфиксным/постфиксным — то, разумеется, нет. А если двумя — дело уже интереснее (при условии, разумеется, что все узлы разные).
    Ну, например, как восстановить дерево, которое было пройдено сначала префиксно, потом постфиксно (самый сложный и интересный случай). Признаюсь сразу: полное восстановление невозможно, ведь ситуации «без левых сыновей» и «без правых сыновей» различить нельзя.

    Если длины не совпадают, СТОП: некорректные данные.
    В префиксном обходе корень в начале, в постфиксном — в конце. Если они не одинаковы, СТОП: некорректные данные.
    Если в обходе один элемент — с этим всё понятно.
    Второй элемент префиксного обхода — левый сын. Ищем его в постфиксном обходе. Если он предпоследний — перед нами та самая ситуация «у дерева один сын», и рекурсивно запускаем алгоритм на обходах без корня.
    В противном случае выкусываем подстроки нужной длины (реально или виртуально), дважды запускаем алгоритм рекурсивно.

    Пример: у нас дерево.
         a
      b    c
    d  e   f

    Префиксный обход abdecf, постфиксный debfca. Корень a, левый сын b, он в постфиксном обходе на третьей позиции. Рекурсивно запускаем алгоритм на парах bde/deb и cf/fc.
    Ответ написан
    Комментировать
  • Как вставить значение переменной после % в printf()?

    @Mercury13
    Программист на «си с крестами» и не только
    printf("%*d", nDigits, value);

    А-а-а, уже было в комментариях!..
    Ответ написан
    Комментировать
  • Почему не работает libcurl?

    @Mercury13
    Программист на «си с крестами» и не только
    Как ни странно, нужна версия libcurl с поддержкой HTTPS.
    Большинству таких версий в нагрузку нужны два файла OpenSSL — libeay32.dll, ssleay32.dll.
    Ответ написан
    Комментировать
  • Можно ли сделать массив меток goto?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы не представляете себе, так обычно и работает switch.
    prog21.dadgum.com/166.html
    Ответ написан
    5 комментариев
  • Как на C++ узнать количество передаваемых аргументов?

    @Mercury13
    Программист на «си с крестами» и не только
    Какие есть способы физически, на уровне машинного кода, передать переменное число параметров в подпрограмму?

    1. Автобоксинг в массив. Если к тому же тип элементов может быть любым — тогда будет массив «лёгких» или обычных variant’ов, или массив объектов на «куче». В Delphi для этого используется лёгкий variant (см. SysUtils.Format), в Java и других «мусорных» языках — объекты на куче. В Си++ нет.

    2. Автоматически развернуть такой вызов в кучу вызовов поменьше. См. вариативные шаблоны C++11 = variadic templates. В Си++ есть, штука тяжёлая, я с ней и сам знаком поверхностно и ничего толком рассказать не могу. Но вот кое-что набросал.
    #include <iostream>
    
    constexpr int countArgs() { return 0; }
    
    template <class Arg, class ... Args>
    constexpr int countArgs(const Arg& x, const Args& ... args)
    {
        return countArgs(args...) + 1;
    }
    
    int main()
    {
        std::cout << countArgs() << std::endl;
        std::cout << countArgs(1, 2, 3) << std::endl;
        return 0;
    }

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

    3. Использовать особые соглашения вызова и раскручивать стек, пока не попадётся какой-то маркер «больше параметров нет» (см. работу с формами cURL), или окольным путём узнать количество параметров (см. printf). Есть даже в Си (который не что иное, как «ассемблер высокого уровня»), штука очень системная и чреватая ошибками.

    4. Возможна ещё и такая фишка: физически оно устроено как printf, но обёрнуто в «лёгкий» вариативный шаблон, который защищает всё это добро от ошибок программиста.
    #include <iostream>
    #include <cstdarg>
    
    constexpr int countArgs() { return 0; }
    
    template <class ... Args>
    constexpr int countArgs(int x, Args ... args)
    {
        return countArgs(args...) + 1;
    }
    
    void outArgsInner(int count, ...)
    {
        va_list ap;
        va_start(ap, count);
        if (count > 0) {
            std::cout << va_arg(ap, int);
            for (int i = 2; i <= count; ++i) {
                std::cout << ' ' << va_arg(ap, int);
            }
        }
        va_end(ap);
        std::cout << std::endl;
    }
    
    
    template <class ... Args>
    inline void outArgs(Args ... args)
    {
        outArgsInner(countArgs(args...), args...);
    }
    
    int main()
    {
        outArgs();      // пустой тоже работает
        outArgs(1, 2, 3);
        // outArgs("a", 2, 3);   тут ошибка! — и верно, мы принимаем только int’ы
        return 0;
    }
    Ответ написан
    Комментировать
  • Как лучше задать табличную функцию с редкой областью определения?

    @Mercury13
    Программист на «си с крестами» и не только
    Если аргументы до 100, функция заранее известна и неизменна — чхайте на всё и делайте простым массивом. Именно так, таблицей переходов, внутри устроен switch. Всё, что я пишу ниже — для общего развития.

    Это называется «разреженный массив» (sparse array)

    1. Хэш-таблица (std::unordered_map).
    Преимущество: есть «в коробке», очень быстра.
    Недостаток: ест много памяти.

    2. Я также использую вот такой механизм.
    template <class T>
    class ChunkMap
    {
    public:
        // Data access
        //------------------------------------------------------------------------//
        /// @return  data at index t, or empty value
        T get(size_t t) const;
    
        //------------------------------------------------------------------------//
        /// Sets data at index t (empty value to erase)
        void put(size_t t, T x);
    
        //------------------------------------------------------------------------//
        /// Erases range [0..maxPlus)
        void eraseTo(size_t aMaxPlus);
    
        // Info
        size_t nChunks() const { return fChunks.size(); }
        bool isEmpty() const { return fChunks.empty(); }
    
        //------------------------------------------------------------------------//
        /// @return  the number that is beyond all chunks
        size_t ceil() const;
    
        //------------------------------------------------------------------------//
        /// @return  actual number of records in the map
        size_t size() const;
    
        //------------------------------------------------------------------------//
        /// @return  lowest value in the map; (0, empty) if empty
        std::pair<size_t, T> lowerValue() const;
    
        //------------------------------------------------------------------------//
        /// @return  highest value in the map; (0, empty) if empty
        std::pair<size_t, T> upperValue() const;
    
        //------------------------------------------------------------------------//
        /// @return  (t1, v), t1 <= t;  (0, empty) if none
        std::pair<size_t, T> lowerOrEq(size_t t) const;
    
        template <class Body> void loop(const Body& r) const;
        template <class Body> void loopFrom(size_t aMin, const Body& r) const;
    
        //------------------------------------------------------------------------//
        ///  Loops all data. Body is bool-convertible.
        ///  Return true → go on.
        ///  Return false → stop and return false.
        template <class Body> bool loopBool(const Body& r) const;
    
        void clear() { fChunks.clear(); }
    
        constexpr static T emptyValue() { return std::numeric_limits<T>::max(); }
        static bool isEmptyValue(const T& x) { return (x == emptyValue()); }
    
        constexpr static unsigned chunkSize() { return Chunk::SIZE; }
    
    private:
        struct Chunk
        {
            enum { SIZE = 8 };
            Fix1d<T, SIZE> data;   // мой шаблон, массив фиксированного размера с проверкой на выход за границы
    
            Chunk();
            bool isEmpty() const;
            std::pair<size_t, T> lowerValue(size_t aKey) const;
            std::pair<size_t, T> upperValue(size_t aKey) const;
        };
        typedef std::map<size_t, Chunk> Chunks;
        Chunks fChunks;
    };

    Можно делать и на unordered_map при желании, но мне нужна lowerOrEq().

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

    3. Вот ещё есть реализация на Java.
    https://android.googlesource.com/platform/framewor...
    Преимущество: серьёзная экономия памяти.
    Недостатки: когда массив велик (тысячи заполненных элементов), снижается скорость доступа. Большие расходы на чистку мусора.
    Ответ написан
    Комментировать
  • Как оценить работу функции которая рассчитывает вероятность наступления события?

    @Mercury13
    Программист на «си с крестами» и не только
    Тогда возникает вопрос. Какие могут быть недостатки у функции?

    1. Смещение к «да» или смещение к «нет».
    Для всех случившихся событий перемножаем вероятности соотв. исхода. То же самое для всех неслучившихся. Одно делим на другое, в идеале должна получиться единица.
    Можно работать с логарифмами, даже очень широкая статистика не даст переполнения: на мантиссу в double шесть байтов, а на порядок — и двух нет. Можно проводить сброс порядка: получили, скажем, число меньше 1e−50 — множим на эту цифру, запоминаем: 50 порядков в уме.
    Если статистики очень много, стоит кластеризовать входы и для каждого кластера посчитать эту статистику.

    2. Излишняя самоуверенность. Функция говорит «0,9», в то время как вероятность максимум 0,7. Думаю, можно решить той самой кластеризацией по выходу функции.

    3. Неуверенная работа. Функция не даёт статистических аномалий, но просто неуверенно работает, слишком часто выдавая «а хрѣнъ его знает». Vlad_Fedorenko предлагает площадь под ROC-кривой. Я бы просто предложил произведение вероятностей соответствующих исходов. Например, если у нас 6 пусков, можно сказать: «вероятность всегда 0,5», и получить результат 1/64≈0,016. А можно сказать: для трёх пусков вероятность 2/3, и для трёх — 1/3. Если так и случится, результат — 24/36≈0,022. Какой будет нормирующий коэффициент, сказать пока не могу.
    UPD3. А ещё можно попробовать информационную энтропию.
    UPD4. Эта конструкция, возможно, справится и с излишней самоуверенностью. Если она скажем этим тройкам 0,9 и 0,1, будет (0,9·0,9·0,1)² < 0,01.
    UPD5. Идеал — разумеется, 1 (событию говорит 100%, если оно будет, и 0%, если нет).

    UPD. Подкорректировал 1 — я так и имел в виду, но проглючил.
    UPD2. Добавил неуверенную работу.
    Ответ написан
    Комментировать
  • Как разделить два числа в Assembler при помощи цикла?

    @Mercury13
    Программист на «си с крестами» и не только
    Хотелось бы уточнить.
    1. Размеры делимого и делителя.
    2. Знаковые или беззнаковые?
    3. Нужен ли остаток?
    4. Делим на константу или на что-то заранее неизвестное?

    Вот для примера 16-битное беззнаковое делить на произвольное 8-битное беззнаковое, как я понял, без остатка.
    www.avr-asm-tutorial.net/avr_en/calc/DIV8E.html

    А вот так работает деление на константу (в данном случае на 6).
    stackoverflow.com/questions/34136339/how-does-divi...
    Ответ написан
  • Как посчитать угол по 2-м координатам?

    @Mercury13
    Программист на «си с крестами» и не только
    Как я понял, вам нужен угол вектора (x1,y1)→(x2,y2).
    Любой школьный «арк», если им действовать в лоб, в определённом диапазоне углов не определён или неустойчив.
    Но именно для этого в большинстве языков присутствует функция
    atan2(y2 - y1, x2 - x1)
    Ответ написан
    Комментировать
  • Что такое call stack?

    @Mercury13
    Программист на «си с крестами» и не только
    Call stack — это стек вызовов, аппаратная фишка всех крупных процессоров.

    int foo()
    {
       int y = ...;
       if (y < 0)
         y = -y;
       return y;
    }
    
    void bar()
    {
       int x = foo();
       if (x != 0)
          bar();
       foo();
    }
    
    void main()
    {
      int z = foo();
      bar();
    }


    Вот мы заходим в функцию main(). В это время в памяти появляется переменная z. А затем в foo() — появляется ещё и переменная y. А затем выходим из foo — y исчезает. Что у нас получается? Либо в конец добавляем одну или несколько локальных переменных, либо с того же конца удаляем их. Какая структура данных подходит для этого? Стек: добавлять в конец, удалять из конца. Всё это, напоминаю, реализовано в x86 аппаратно, одним сегментом, двумя регистрами (esp = 32-bit stack pointer и ebp = 32-bit base pointer) и несколькими командами (push, pop, call, ret…).

    Локальные переменные, принадлежащие одной функции, называются стековый фрейм. При этом фреймов одной функции может быть несколько, если она вызывает сама себя, прямо или опосредованно — как функция bar().

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

    Поскольку стек аппаратный, его нельзя расширять; можно только до начала программы/потока указать, сколько памяти нам хватит. И потому часта ошибка «переполнение стека».
    Ответ написан
    Комментировать
  • Почему возникает ошибка LNK2001 Неразрешенный внешний символ символ?

    @Mercury13
    Программист на «си с крестами» и не только
    Как говорят, «или крест снимите, или трусы наденьте». И учите понятие «единица компиляции».
    По какой схеме устроен ваш проект? «Одна единица компиляции» или «много единиц компиляции»?

    Си недалеко ушёл от ассемблеров. А в ассемблерах программа компилировалась по частям и собиралась воедино линкером (компоновщиком, редактором связей) — в те времена кода было много, а данных мало. Многие из ошибок невозможно было определить, не запустив линкер. Си++ пользуется многими из архитектурных особенностей ассемблеров и Си — по крайней мере ни одно из модульных решений не стало рекомендацией (кроме костыля extern template class).

    Но как говорить «переменная/функция есть, такого-то типа и в другой единице компиляции»? Для этого есть прототипы функций и extern-определения переменных. Их обычно вносят в заголовочные файлы с таким требованием: ничего, что находится в заголовочном файле, не должно производить кода. А код производят…
    • Глобальные переменные (без typedef, extern).
    • Нешаблонные функции (кроме inline).
    • Полностью специализированные шаблонные функции (кроме inline).
    • Команда «специализировать шаблон» (template class).

    При этом…
    • Функции в теле struct/class автоматически inline и кода не производят.
    • Для неявной специализации шаблонов существуют обходы — код генерируется дважды, но ошибки не выдаёт.
    • «Свой» хедер обычно включают первым, чтобы убедиться, что в нём нет недостающих зависимостей.

    В системе «одна единица компиляции» всё просто: есть ровно один файл, подлежащий компиляции. Тогда в хедерных файлах вполне могут быть конструкции, производящие код.

    Системы «одна единица компиляции» и «много единиц компиляции» можно комбинировать, но надо знать:
    • Все хедеры, которые производят код, должны подключаться из одной-единственной единицы компиляции. Надо чётко осознавать, из какой, и не подключать из чужих.
    • У библиотеки всё равно должен быть хедер-фасад, не производящий кода и предназначенный для стыковки с другими единицами компиляции.
    Такая конструкция ускоряет полную перекомпиляцию и часто применяется для библиотек, но надо знать: огромные библиотеки вроде SqLite, в 5 мегабайт препроцессированного кода, мешают распараллеливанию компиляции (ибо пока откомпилируется SqLite, остальные процессоры вполне себе соберут остальную программу).

    // В схеме «одна единица компиляции»: ничего не делать.
    // В схеме «много единиц компиляции»: лишняя зависимость; унести в unit1.cpp
    #include <cstdlib>
    
    // В схеме «одна единица компиляции»: убрать extern.
    // В схеме «много единиц компиляции»: завести unit1.cpp, там сделать MyType x;
    extern MyType X;
    
    // В схеме «одна единица компиляции»: ничего не делать.
    // В схеме «много единиц компиляции»: перенести функцию в unit1.cpp, оставив в .h только прототип.
    void XReset() {}
    Ответ написан
    Комментировать
  • Лучше ли книги онлайн - курсов?

    @Mercury13
    Программист на «си с крестами» и не только
    Онлайн лучше.
    1. Объяснить сложный принцип действия, когда видео действительно подспорье. Например, рассказать в динамике о том, как работает сложный шифропротокол.
    2. Рассказать о простых стандартных задачах. Впрочем, и в этом я не уверен.
    3. Ткнуть носом в нужное место интерфейса.

    Печатный материал лучше.
    1. Когда нужно посидеть и осознать. Первые шаги в вашем первом языке программирования, когда даже синтаксис стена. Практически всё, что относится к алгоритмам.
    2. Работа с низкоуровневыми библиотеками наподобие OpenGL или SDL, которые производят кучу кода.
    3. Небольшой справочник.
    Ответ написан
    Комментировать
  • Как определить метод класса, чтобы объект в него передавался не по ссылке?

    @Mercury13
    Программист на «си с крестами» и не только
    UPD. Теперь понял, о чём вы. В таком виде нельзя.
    Ответ написан
  • Как работать со строковыми матрицами?

    @Mercury13
    Программист на «си с крестами» и не только
    Простейший способ — отсортировать каждую строку обеих матриц. Строки, изначально являвшиеся анаграммами, будут совпадать, и теперь уже можно выяснять, что с чем совпадает. Сложность O(w·(hA+hB)·max{hA+hB, log w})

    Более сложный (лучше асимптотическая оценка). После того, как отсортировали каждую строку, строки матрицы B дополнительно переупорядочиваем в лексикографическом порядке (ААА < ААБ <…< ААЯ < АБА). Для поиска совпадений пользуемся двоичным поиском. Сложность O(w·(hA+hB)·log max{w, hA, hB}).

    Можно также каждой строчке матрицы B посчитать хэши и запомнить в какой-то структуре данных: хэш не совпал ни с чем — полное сравнение не проводится. Не улучшает оценки в худшем случае, но улучшает в среднем.

    Офтоп. Было дело, я оптимизировал WAD’ы Doom’а по размеру (формат позволял нескольким блокам ссылаться на один и тот же участок файла, а во многих модах — и даже в самом Doom’е — были повторы). Обошёлся хэшированием, и хэшем был размер + несколько байт из середины блока.
    Ответ написан
    Комментировать
  • Почему код сортировки работает не так как ожидается?

    @Mercury13
    Программист на «си с крестами» и не только
    Первый критерий, по которому сортируем,— уровень.
    То есть, сначала корень, потом его потомки, потом потомки потомков…

    Второй критерий — коды родителей. Точно вам надо сортировать по кодам, а не по текстам?

    И третий — коды самих пунктов. Аналогично.

    Итого. Будет сначала корень, потом его потомки по кодам, потом их потомки по паре «код предка → свой код», и т.д.
    Ответ написан
  • Как Работает вирус?

    @Mercury13
    Программист на «си с крестами» и не только
    Добавлю от себя.

    Сейчас вирусы на ассемблере пишут редко, и этому есть две причины.
    1. Сложная полезная вредная нагрузка: загрузка чего-то через Интернет, сетевые эксплойты, дистанционное управление.
    2. Сложный формат PE, крайне редкий обмен софтом, цифровые подписи на критических файлах и UAC. Потому встраиваться в файлы тяжело и малопродуктивно. UPD — существует немало «вирусов-спутников», но цифровые подписи на критических файлах и UAC не пробить и им.

    Эксплойту с внедрением кода нужно небольшое тело, и его пишут на ассемблере или Visual C. А если это обычный EXE/DLL — то на языке, для которого в ОС гарантированно есть runtime-среда. В основном Visual C и .NET старых версий.

    Вирусы, эксплуатирующие ошибки в runtime-средах (JS-движках, Java, .NET) — разумеется, на языке среды (JS/Java/C#). Если ошибка предполагает внедрение кода — см. выше.

    UPD2. Я не писал вируса, но как бы я его заставил работать?

    Прошёлся бы по дискам. Создал бы скрытую папку с микрософтоподобным именем и потихонечку бы шифровал в неё файлы. Вдруг в момент Икс я переименовываю исходные файлы в ту самую папку, затираю пару килобайт начала, переименовываю скрытые на их место и вывожу: ваш компутер зашифрован! Если человек мешкает — затираю остатки файлов, а потом и удаляю их.

    Остаётся придумать, как этот вирус протащить на компьютер жертвы и как прикинуться легитимной программой. Простейший вариант, по-моему — прикинуться сайтом программ, в каждую поместить вирус и сделать активное SEO. Что будет, когда попадусь — не знаю. А прикинуться программой — придумаю какую-нибудь службу-технотуфту от Microsoft. Хотя настоящие вирусописатели покупают на чёрном рынке эксплойты, я с этим делом не знаком.
    Ответ написан
    Комментировать
  • Как производить вычисления ip-адресов силами только sql?

    @Mercury13
    Программист на «си с крестами» и не только
    IpAddress & Mask = IpAddr
    (при условии, что IP гарантированно настоящий и не надо узнавать, это настоящий адрес или один из сервисных, связанных с сетью)
    Возможно — я не знаю, каких размеров таблица — БД держат в таком виде ради индексирования.
    Ответ написан