• Как зайти в bios роутера?

    @Mercury13
    Программист на «си с крестами» и не только
    Что такое BIOS? Это базовая система ввода-вывода.
    Какие функции выполняет BIOS на обычном ПК?
    1. Инициализация и проверка работоспособности матплаты — чтобы этим не занималась ОС с постороннего носителя.
    2. Начальная загрузка с постороннего носителя.
    3. Софт вроде Бейсика, доступный без носителя.
    4. Простейший API, позволяющий легче обращаться к консоли, дискам и прочему.
    5. Конфигурирование внешней аппаратуры.
    На встраиваемых системах нет во всём этом надобности, в отличие от расширяемого ПК.
    Разве что может быть конфигурирование аппаратуры — но для этого в некоторых микроконтроллерах (AVR) используют конфигурационные биты, они же «фьюзы». Ну а для конфигурирования внешнего по отношению к МК — только прошивка.
    А вот поковырять прошивку можно, если система позволяет. Как эту прошивку слить и залить — это отдельный вопрос.
    Ответ написан
    Комментировать
  • В какого типа переменных хранить адреса?

    @Mercury13
    Программист на «си с крестами» и не только
    Вариантов много.
    1. Непрозрачные указатели, которые нельзя разыменовывать.
    struct OpaqueAddress;
    using Address = OpaqueAddress*;

    2. void*, const void*.
    3. uintptr_t.
    4. Жёсткие int’ы, если работаем с конкретной посторонней прогой под конкретную архитектуру (например, пишем чит к игре).
    using Address = uint32_t;
    5. Enum class, основанный на соответствующем int’е.
    enum class Address : uintptr_t { NUL = 0 };
    Ответ написан
    Комментировать
  • Что стоит учить с или c++ или c#?

    @Mercury13
    Программист на «си с крестами» и не только
    Если ты умеешь программировать на чём угодно, хоть на Скрэтче — можно любой по желанию. Разберёшься.
    Если с нуля — только C#, на нём меньше шансов напортачить. Главная проблема Си, плохо решённая в Си++,— для простых вещей приходится работать со сложными концепциями вроде указателей для scanf.
    Ответ написан
    Комментировать
  • Есть ли реальная необходимость использовать Git LFS?

    @Mercury13
    Программист на «си с крестами» и не только
    У Git-LFS только два выигрыша.
    1. Перескок от ветки к ветке происходит быстрее.
    2. Многие хостинги имеют отдельную политику для крупных файлов, что позволяет как-то жить, не упираясь в пределы дискового места.
    Ответ написан
    Комментировать
  • Почему '\xDA', '\xc4', и другие управляющие последовательности не работают?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что вы перевели консоль в кодировку Win-1251 и и ждёте в ней псевдографику.
    Лучше всего работать в каком-то из вариантов Юникода.
    Ответ написан
    Комментировать
  • Как нормализовать массив значений в цветовое представление?

    @Mercury13
    Программист на «си с крестами» и не только
    Уровень 1. Чёрно-белое.
    R, G, B = round((x−min)·255 / (max − min))

    Уровень 2. Градиент между цветом X и цветом Y.
    t = (x−min) / (max − min)
    R = round(R1·t + R2·(1−t))
    G и B аналогично.

    Уровень 3. Учёт гамма-кривой монитора. Тут работаем сразу в двух цветовых пространствах: линейном от 0 до 1, и sRGB от 0 до 255.
    gamma = 2,2 — sRGB состоит из линейного и степенного участка, но неплохо приближается real_brightness = channel_%^gamma
    invGamma = 1/gamma
    функция toLinear(v) := (v/255)^gamma
    функция toSrgb(q) := (q^invGamma)·255 — в общем, обратная
    linR1 = toLinear(R1)
    linR2 = toLinear(R2)
    t = (x−min) / (max − min)
    R = round(toSrgb(linR1·t + linR2·(1−t)))

    Уровень 3.1. 16-битная аппроксимация (если важна скорость). В общем, линейное цветовое пространство — не дробные от 0 до 1, а целые от 0 до 65535.
    функция toLinear(i) := round(((i/255)^gamma)·65535)
    массив toSrgb(q) := ((q/65535)^invGamma)·255 — записывается в виде массива на 65536 величин
    linR1 = toLinear(R1)
    linR2 = toLinear(R2)
    K = (65535 / (max − min)
    t = round((x−min) * K)
    R = toSrgb(((linR1·t) + linR2·(65535−t) + 127) >> 16)

    Уровень 4. Сложный градиент из нескольких цветов.
    Для этого например, t=0 — синий, t=⅓ — зелёный, t=⅔ — жёлтый, t=1 — красный.
    Тогда прикидываем, в какой промежуток попадает t, получаем, например, t1=(t−⅓)·3, и дальше по уровню 3.
    Ответ написан
    Комментировать
  • Мне надо иметь две .lib для debug и release?

    @Mercury13
    Программист на «си с крестами» и не только
    Debug-версия библиотеки может помочь отлаживать какую-то дичь на границе вашего и библиотечного кода. Я лично не пользовался ни разу, всюду подключал release-версию.
    UPD. Я не работал с MSVC, но этот самый MSVC имеет разные runtime-библиотеки для отладки и выпуска. И они не хотят соседствовать друг с другом. Это уже внутренние разборки MSVC; две библиотеки MinGW, статическую и динамическую, но обе release, вполне себе позволяет и для некоторых целей (упрощение отладки на той самой границе) очень даже помогает. SDL2, как и большинство открытых библиотек, имеют минимальные зависимости и ему просто не нужна библиотека MSVC.
    Ответ написан
    Комментировать
  • В чем ошибка блок-схемы?

    @Mercury13
    Программист на «си с крестами» и не только
    Если считать, что прога верная, а блок-схема нет.

    1. Непонятно, почему ввода нет, вывод есть.
    2. Начальное присваивание smallest опущено.
    3. Очень креативно изображён двойной цикл. Не знаю, верно ли это — но пару вложенных циклов, если рамки внутреннего не зависят от того, что творится во внешнем и не нужно прерывать один внутренний, я бы написал оба в одном шестиугольнике.
    4. Условие внутреннего цикла я бы написал i=[0..n)
    5. В программе сначала выводим сообщение, потом матрицу. На БС наоборот.

    PS. Чёртов препод, ну не годится тут блок-схема, и точка. Может, она больше годилась, когда не было структурного программирования…
    Ответ написан
    Комментировать
  • Что делать если разработали и выложили в Сеть программу с таким же названием, как у меня?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Если он ухитрился сделать клон ИЗ ТВОЕЙ ПРОГРАММЫ — тогда прижать за авторское. Факт нарушения авторского права определить сложно, вероятность мизерная.
    2. Если он написал свою и зарегистрировал товарный знак — переименоваться.
    3. А если он, жадина такая, не зарегистрировал — беги регистрировать.
    Ответ написан
  • Как сократить данную операцию?

    @Mercury13
    Программист на «си с крестами» и не только
    Сначала законы де Моргана. Потом !!B=B, и т.д.
    Первая часть даст B || !C. Вторая — !B || C. И всё это ИЛИ — так что выходит тождественная истина (B || !B = 1).
    (Простите, облом искать юникодные символы, так что беру из Си: && и, || или, ! не)
    Ответ написан
    1 комментарий
  • Как правильно округлять числа с плавающей точкой с заданной точностью?

    @Mercury13
    Программист на «си с крестами» и не только
    Гуглите GRISU2 — и, возможно, переделайте этот код под свои нужды.
    Он работает на целой арифметике и делает вот что: находит самое круглое десятичное число, переводимое в компьютерное дробное x.

    Допустим, мне приходилось добавлять такое.
    1. В случае не очень большого числа, большего 1/относительная_погрешность — поднять точность до единиц.
    Например, переходить в экспоненциальный формат, когда у нас больше 5 цифр, а точность 3 цифры — то 0,00123, 0,0123, 0,123, 1,23, 12,3, 123, 1234, 12345, 1,23e5.

    2. Большее из абсолютной и относительной погрешности.
    Если у нас ещё две цифры абсолютной погрешности — то 0, 0,01, 0,12, 1,23, 12,3…

    3. Формат с фиксированной длиной дробной части по принципу «всё или ничего».
    С теми же двумя цифрами абсолютной погрешности — 0, 0,01, 0,12, 1,23, 12,34, 123…

    4. Работу с локалями.
    Ответ написан
    Комментировать
  • Почему множественный вызов конструктора создаёт только один объект?

    @Mercury13
    Программист на «си с крестами» и не только
    Прочитайте про избавление от копирования, которое сделали обязательной функциональностью Си+17.
    https://en.cppreference.com/w/cpp/language/copy_elision
    Ответ написан
    Комментировать
  • Зачем нужен интерфейс, если есть абстрактный класс?

    @Mercury13
    Программист на «си с крестами» и не только
    Разрешите добавить. Интерфейс, грубо говоря,— это абстрактный класс без данных и недописанных функций (каждая или полностью жизнеспособна в какой-то ситуёвине, или нулевая = полностью абстрактная, не путать с пустой). И причины этому две.

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

    2. Техническая. Прикрывает один такой серьёзный жупел, как ромбическое наследование данных. Ромбическое наследование данных бывает двух видов.
    Пусть у нас такое:
    class Grandfather { public: int field; };
    class LeftFather : Grandfather {};
    class RightFather : Grandfather {};
    class Son : LeftFather, RightFather {};

    а) С дублированием, когда у сына два поля field, унаследованное через левого отца и через правого. Достаточно сделать функцию…
    void foo(LeftFather& x) { x.field = 42; }
    Как сохранить синхронизацию для левой и правой ветки наследования?
    б) С общим дедом, когда у сына одно такое поле (через виртуальное наследование Си++). Тут левая и правая ветки могут быть просто не готовы к тому, что поле будет меняться без её ведома.

    Ромбическое наследование функций не так вредно: ведь любая функция, извините, работает с данными. А значит, она или имеет дело с внешними данными (а значит, в обеих ветках сделает одно и то же), или нулевая, или просто комбинация таких же нулевых (InputStream.remainder() { return size() - pos(); }).

    Почему я говорю «в какой-то ситуёвине». Возьмём тот же remainder. Существуют потоки — скажем, закэшированный ввод с внешнего устройства — которые не имеют размера и позиции, но способны иметь остаток. Для каких-то потоков можно написать остаток более эффективный. Но это казуистика.
    Ответ написан
    Комментировать
  • Может ли вирус попасть в микроконтроллер?

    @Mercury13
    Программист на «си с крестами» и не только
    Может, но для этого нужны очень много предпосылок.
    1. Микроконтроллер должен уметь обновлять своё ПО без вмешательства пользователя.
    2. Очень желательно, чтобы он умел обновлять ПО кусками, чтобы вирус не включал целую действующую прошивку.
    3. Микроконтроллер должен иметь связь с миром через цифровые каналы — да хоть через флэшку.
    4. Эти цифровые каналы должны иметь такую уязвимость, чтобы этот микроконтроллер запустил функцию обновления прошивки.
    5. Вирус должен быть написан под конкретный микроконтроллер.

    Во многих СЛАБЫХ микроконтроллерах есть разделение памяти на программную и данных — даже если уязвимость есть, она не сможет внедрить свой программный код, придётся искать подходящую функцию в имеющейся прошивке.

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

    Уязвимые роутеры есть, даже у D-Link’а попадался заражаемый. Но это не универсально, и надо смотреть под каждое устройство по отдельности.
    Ответ написан
    Комментировать
  • Как хранится c++ struct в памяти и как определить размер вручную?

    @Mercury13
    Программист на «си с крестами» и не только
    Выяснилось, что на компьютерах с разрядностью 16 и более бит минимальной единицей памяти стоит делать всё равно 8-битный байт: это сильно упрощает работу с узкими типами данных, как в памяти, так и на вводе-выводе. Например, канал RGB8 или кодовая единица UTF-8 — байт. Считаем для простоты, что разрядность 16, порядок Intel.

    Чтобы загрузить 2-байтовое число в 2-байтовый регистр, есть ДВА варианта.
    1. ОЗУ, кэш и другие устройства хранения передают нижний байт данных на нижний байт шины, верхний, соответственно, на верхний.
    2. Байты, чьи номера кратны двум, соединяются только с нижним байтом шины, не кратные — только с верхним. А уж процессор как-то разбирает, что делать, если двухбайтовое число сидит по нечётному адресу и считывается за два обращения к шине.

    Более простым оказался второй подход: он упрощает схемотехнику всех сидящих на шине, кроме процессора. В частности, дешифраторы становятся на 1 бит меньше, и упрощается топология. А процессор — в нём и так микропрограммное управление, и в нём и так до хрена соединений, и если обратился к байту (!) по адресу условно 4, вероятно, скоро потребуется адрес 5 и его один хрен стоит закэшировать.

    Но это значит, что доступ к 2-байтовым числам по нечётному адресу (1-2, 3-4, 5-6) будет медленнее, чем по чётному (0-1, 2-3, 4-5). И потому по умолчанию двухбайтовые числа располагаются именно по ЧЁТНЫМ адресам, и структура { char a; short b; char c; } будет иметь длину 6 байт (0 — a, 1 — выравнивание, 2,3 — b, 4 — c, 5 — выравнивание), а структура { short b; char a; char c; } — всего 4 байта (всё упаковано плотно).

    Но это приводит к двум вещам.
    1. Если нужно сочетать скорость и компактность, надо чётко выравнивать структуры данных. Обычно подобная ручная оптимизация делается для мелких частых структур.
    2. Если кусок памяти подготавливается в памяти, а потом отправляется на устройство, надо принудительно отключить выравнивание. Если в предыдущем примере формат файла так устроен, что байт 0 — a, байты 1 и 2 — b, байт 3 — c, то можно написать
    #include <iostream>
    
    struct Q1 { char a; short b; char c; };
    struct __attribute__ ((packed)) Q2 { char a; short b; char c; };
    
    int main()
    {
        std::cout << sizeof(Q1) << ' ' << sizeof(Q2) << '\n';   // 6 4
    }

    Q1 (или более оптимизированную short-char-char) использовать для хранения в памяти, а Q2 — для записи в файл, раз уж у него такой формат.

    Да, почему компилятор сам этого не делает? Это нарушает совместимость компиляторов. В Си++ до 20 включительно он имел право кое-что переставить, если в struct/class были поля с разными правами доступа (но никто по факту не делал), в 23+ больше не имеет права.

    Ну а алгоритм прост.
    1. Длина := 0, выравнивание := 1
    2. Для каждого поля: позиция := длина, округлённая по выравниванию[i]; длина := позиция+длина[i]; выравнивание := max{выравнивание; выравнивание[i]}
    3. Общая длина := длина, округлённая по общему выравниванию

    Существует очень простой способ уменьшить выравнивание: отсортировать поля по убыванию выравнивания.

    Для того же Q1:
    0. Длина = 0, выравнивание = 1
    1. char a: длина округляется до 0, затем 1, выравнивание = 1
    2. short b: длина округляется до 2, затем 4, выравнивание = 2
    3. char c: длина округляется до 4, затем 5, выравнивание = 2
    4. Общая длина округляется до 6. Ну а выравнивание = 2
    Ответ написан
    Комментировать
  • Нахождение F(x), для которой аргумент (элем. исход) ξ(ω) распределён неравномерно?

    @Mercury13
    Программист на «си с крестами» и не только
    Всё точно так же. Считаем, что φ от −π/2 до π/2!!!!
    Fx(u) = p{x < u} = p{h tg φ < u} = p{φ < arctg(u/h)} = Fφ(arctg(u/h))

    Соответственно, в твоём случае φ от 0 до π, при непрерывном распределении:
    Fx(u) = p{x < u} = p{h ctg φ < u} = [арккотангенс убывает, меняется знак!!] = p{φ > arcctg(u/h)} = 1−Fφ(arcctg(u/h))

    Лучше брать arcctg(x/h), чем arctg(h/x) — не нужно делать скидку на то, что мы сидим на двух стульях кусках ОДЗ.
    655408c964d68977789634.png
    Ответ написан
  • Что и почему лучше подключить в C++? math.h или cmath? stdio.h или cstdio?

    @Mercury13
    Программист на «си с крестами» и не только
    math.h — это для кода, который должен быть одновременно Си и Си++. Также разглючка в некоторых версиях Embarcadero.
    cmath — рекомендуется в Си++.
    Ответ написан
    Комментировать
  • Возможно ли реализовать одноранговый мультиплеер с защитой от жульничества?

    @Mercury13
    Программист на «си с крестами» и не только
    Это не одноранговая игра, это клиент-серверная.

    ИДЕЯ 1. Передавать с сервера картинку. Всё, читерство пресечено на корню. Это те самые Google Stadia и PS Now. Недостатки — в огромной нагрузке на сервер и задержках между управлением и картинкой. Как их решить?

    ИДЕЯ 2. Передавать с сервера некую информацию, по которой клиент будет строить картинку. Нагрузка на сервер меньше — он не занимается рендерингом. Зато появляется первая предпосылка к жульничеству — по этой информации жульническая программа определяет, где находится враг, и наводит оружие. Но остаётся недостаток: всё та же огромная задержка между управлением и картинкой. При этом задержку никак нельзя улучшить — у нас то самое «пространство-время», знакомое из теории относительности.

    О чём я: между Землёй и Луной секунда, и случились два события, на Земле и на Луне. Возможны три варианта: а) свет доходит от Земли до Луны, и событие на Земле заведомо раньше для любого наблюдателя; б) свет доходит от Луны до земли, и событие на Луне заведомо раньше; в) не успевает дойти ни тот, ни другой свет, и хрен его знает, какое раньше — для одних наблюдателей земное, для других лунное. Подобное пространство-время есть и в любом динамичном мультиплеере.

    ИДЕЯ 3. Ну, если задержку не улучшить, купируем её тем, что клиент может как-то предсказывать, чтó остальные будут делать, пока пакет ползёт от сервера. Чего не хватает? 1) Клиенты могут внезапно появляться в поле зрения, и не очень хотелось бы, чтобы мы зашкерились за углом, а враг появился ХЗ где, причём где — это зависит от нашего пинга; 2) Из соображений серверной производительности вопрос «видит ли клиент А клиента Б?» желательно решать очень приблизительно, с запасом — Z-буфер решит эту задачу значительно лучше, чем вычисление на процессоре, видит ли клиент А мизинец клиента Б.

    Есть ещё такая штука: представьте себе, у клиента А пропала связь. Он со своей пропавшей связью начинает крутить мышью, и не хотелось бы, чтобы в момент появления связи этот клиент умер от ХЗ кого, кого он даже не видел. Ну или со связью в порядке, но А очень насобачился работать мышью. Так что вопрос «видит ли один другого» обычно не рассматривает направление взгляда, только геометрию уровня.

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

    Вот, собственно, и причина wallhack’ов: сервер принял решение, что клиент А, возможно, видит клиента Б, и начинает передавать его положение. И потому wallhack’и обычно не работают, если Б далеко: сервер понимает, что Б не виден и в ближайшее время не будет виден.

    Есть ещё такая помощь клиенту, связанная с тем, что есть мощные виды оружия вроде снайперской винтовки, с огромной разницей между «попал» и «промазал». Каждый клиент видит своё в зависимости от работы сети (вспомните фразу «ХЗ, что раньше»), и в момент прихода информации о выстреле сервер пытается восстановить, что именно видел клиент на своём экране, и по этой картинке решает, попал или нет (на этом основан глюк «спрятался за углом, и всё равно убили»). Но эту помощь тоже эксплуатируют: представьте себе, пакеты клиент→сервер, которые идут по штуке за, скажем, 50 мс, отправляют по пачке за 0,3 секунды — больше типичного времени компенсации задержек, но меньше типичного времени реакции. Получается, что персонаж скачет (клиенты, заглядывающие наперёд на 100 мс, отказываются предсказывать дальше — пусть он лучше зависает, чем тыкается в стены), причём скачет так, что в него хрен выстрелишь (пока среагируешь, он перескочит в другое место). А поскольку пакеты сервер→клиент ходят исправно, а сервер руководствуется прикидкой, что видел клиент, клиент всё видит и метко стреляет, а в него никто попасть не может. Если этот трюк исполнять автоматически только при появлении врага на экране, это будет максимально беспалевно — ну у игрока начались перекэширования из-за того, что видеопамяти не хватило. Эту штуку можно сделать даже на приставке, даже не разбирая её — нужен свитч (приставки всё-таки насобачились сообщать, что кто-то физически режет провод), кусок витой пары, моторчик с оптопарой и педалька под ногу. А для некоторых игр хватает свитча и педали, главное не жадничать и отпускать её иногда.

    А теперь вместо «чечни» (я так обозвал Counter-Strike — он появился в те времена, когда война в Чечне превратилась в антитеррористическую операцию) возьмём какой-нибудь преферанс. В чём разница? А в том, что игра не динамичная! И потому тактика «передавать только те данные, что нужны для рендеринга» вдруг становится годной. И если вы в преферансе сможете палить чужие карты — автор разгильдяй!

    Скажу честно, я не знаю, как на всех этих chess.com компенсируют задержки в шахматном суперблице типа «три минуты на партию». Если средняя игра тридцать-сорок ходов, то задержки в 0,1 секунды отъедят 3…4 секунды, а задержки в 0,2 — 6…8. Но из-за шахматного жульничества и далеко ушедшей дебютной теории основной формат игры — именно суперблиц.

    В общем, хрен вы реализуете диплом о защите от жульничества, не понимая, как работает мультиплеер и откуда жульничество берётся. А берётся жульничество из трёх предпосылок: 1) участники мультиплеера живут в релятивистском пространстве-времени; 2) очень хочется экономить вычисления на сервере; 3) жульничество иногда маскируют под обычные для компьютерного мира явления вроде сетевого затора или долгого кадра.

    Да, как реально защищают от жульничества: 1) Не допускают никого постороннего в программу-клиент, чтобы даже пакеты нельзя было расшифровать; 2) Ловят программы-жулики по принципу антивируса; 3) Передают клиентам как можно меньше того, что они знать не обязаны; 4) Ловят признаки компьютерного вмешательства в изображение или управление.
    Ответ написан
    Комментировать
  • Как реализовать побитовый сдвиг чисел, которые записаны как строки ( длинные числа хранятся в строках)?

    @Mercury13
    Программист на «си с крестами» и не только
    Как я понял, вы пишете калькулятор. В десятичной системе, чтобы вычисленное калькулятором совпало с вычисленным на бумажке — на что ещё десятичная система? Есть два варианта.
    1. Преобразовать в двоично-длинный формат (264 не входит в long long), сдвинуть, вернуть.
    2. Получить 232 в десятично-длинном формате, и перемножить.
    3. В зависимости от величины сдвига поступить так или этак: например, сдвиги на 1/2/3 вторым способом, а на 4 и больше — первым.
    Думайте сами, что лучше.
    Ответ написан
  • Как использовать потоки в современном C++ в приложении на основе цикла событий?

    @Mercury13
    Программист на «си с крестами» и не только
    Если вы пишете работу на чистом Си++ — придумайте некий интерфейс (в объектном смысле) на чистом Си++ для связи с интерфейсом (пользовательским, написанным на каком-то Qt): передать состояние, выяснить, хочет ли юзверь чего-то и так далее.

    Я считаю, что лучше использовать механизмы наподобие почтовых ящиков, при этом для передачи информации из потока в интерфейс использовать PostMessage и обёртки над ним вроде Qt QueuedConnection, возможно, с дополнительной структурой данных наподобие atomic<unique_ptr<SomeData>>).

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

    А future нужно использовать только при взаимодействии потоков-работников, чтобы один подождал, если ему позарез нужен результат второго.
    Ответ написан
    Комментировать