• Почему char - 1 байт, а символьный литерал ('A') - 4?

    @Mercury13
    Программист на «си с крестами» и не только
    А теперь скажу правильный ответ.
    В Си символьный литерал имеет тип int и потому его sizeof 4 байта.
    В Си++ у него тип char и 1 байт. Потому те, кто создавал CPP-файл, проблемы не видели. Очевидно, связано с перегрузкой функций: как-то не хочется, чтобы в foo('A') вызывалась версия для int.
    #include <stdio.h>
    
    int main()
    {
        int sz = sizeof('A');  // латинское
        printf("sz = %d\n", sz);
        return 0;
    }

    Си: 4
    Си++: 1

    При написании char test='A' на стеке будет 1 байт (+выравнивание). Здесь Си, грубо говоря, проводит преобразование типа — прямо при компиляции. Если написать char test=L'Й', сообщит, что преобразование при компиляции ushort→char обрежет результат с 1049 до 25.
    Ответ написан
    Комментировать
  • Сложность алгоритмов для определения подстроки в строке?

    @Mercury13
    Программист на «си с крестами» и не только
    H = |haystack|, n = |needle|, a — размер алфавита.
    Примитивный алгоритм: предобработки нет, работа в среднем 2H, максимум O(Hn).
    Типичные быстрые алгоритмы: предобработка O(n) или O(n+a), работа в среднем <H, максимум O(Hn).
    Автоматные алгоритмы: предобработка O(na) или O(n+a), работа =H.
    Существуют СТРАШНЫЕ алгоритмы с работой в среднем <H и максимум O(H), где применяются — не знаю.
    Если нужно проводить несколько поисков одной «иголки» в разных «стогах», нужна всего одна предобработка.
    Ответ написан
    Комментировать
  • Как работает Hmac?

    @Mercury13
    Программист на «си с крестами» и не только
    HMAC — это так называемая имитовставка.
    То есть нечто среднее между хэш-функцией и электронной подписью.
    Тот, кто изменил сообщение и не знает ключа, НЕ МОЖЕТ подделать имитовставку — в отличие от хэш-функции.
    Но ключ подписи и проверки ОДИН И ТОТ ЖЕ — в отличие от электронной подписи.

    В современных веб-службах имитовставка используется для одного из вариантов аутентификации: сообщения закрыты прочным транспортным протоколом, но нет возможности проверить, тот ли человек на другом конце провода. Для этого подписываем всё тело запроса, и сервер, владеющий тем же ключом, сможет убедиться, что запрос сформирован нужным человеком.
    Ответ написан
    7 комментариев
  • Какие есть в Qt способы синхронизировать кусок кода с интерфейсным потоком?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    К сожалению, только сигналы — и ключевое слово BlockingQueuedConnection.
    А всё остальное — это как сделать синхроблок рядом с процедурой, а сигнал-слот — где-нибудь в главном окне. Скажем, так.

    class AsyncSimpleContext {  // интерфейс
    public:
      virtual void syncExec(const Runnable& body) = 0;
      template <class Body>
      void syncExecT(const Body& body); // тело упущу, тут всё стандартно,
                                             // идиома «виртуальный шаблон»
    }
    
    class FmMain : public AsyncSimpleContext
    {
      // Разрешите не писать реализацию syncExec — тут всё просто:
      // в конструкторе соединить сигнал со слотом методом BlockingQueuedConnection
      // в syncExec проверить id потока и или вызвать прямо, или возбудить сигнал.
    signals:
       void sigSyncExec(const Runnable&);
    private slots:
       void slotSyncExec(const Runnable&);
    }


    …и передавая куда-нибудь как-нибудь интерфейс AsyncSimpleContext, мы можем синхронизировать что-то с интерфейсом.
    Ответ написан
    Комментировать
  • Что значит после класса?

    @Mercury13
    Программист на «си с крестами» и не только
    Шаблонный класс.
    Гугли «Java Generics»

    String — Input parameter type
    Integer — progress report type
    String — result parameter type
    Ответ написан
    Комментировать
  • Как запустить код Си в проекте С++?

    @Mercury13
    Программист на «си с крестами» и не только
    не удается преобразовать 'char*' в 'int*' для аргумента '1' в 'int

    Как правило, ошибка. Функция хотела указатель на 4-байтовые слова — а мы передаём ей указатель на байты.
    Если очень нужно рассматривать int* как char* (например, работаем не с данными какого-то определённого типа, а просто с байтами в памяти) — используйте reinterpret_cast.

    недопустимое преобразование из 'int*' в 'int'

    Почти всегда ошибка. Функция хотела число — а мы ей передаём ей указатель.
    Если реально по какой-то причине нужно значение указателя воспринимать как число — используйте reinterpret_cast.

    Возможен и другой вариант — вы просто упустили операцию «разыменовать» *.

    warning: narrowing conversion of '143' from 'int' to 'char' inside

    Точно ошибка — 143 не вписывается в char (−128…127). Если очень надо, используй static_cast.

    Покажите лучше код, и я устрою ему ревизию.
    Ответ написан
    Комментировать
  • Как пакетно отредактировать множество фотографий?

    @Mercury13
    Программист на «си с крестами» и не только
    XnView — там в диалоге «Batch processing» есть add text и watermark.
    Ответ написан
    Комментировать
  • Зачем делают разные сайты для мобильной и десктопной версии веб-приложений?

    @Mercury13
    Программист на «си с крестами» и не только
    Сайт может быть:
    • преимущественно настольный (Посмотре.ли);
    • преимущественно мобильный (Эгея. Или вот знакомая пишет внутреннюю систему учёта отпускных — всё сделано, чтобы работало с мобилы);
    • адаптивный (Розетка — простите, я с Украины. Ну и Хабр Q&A);
    • с сильно разными шкурками под настольный и мобильный (Википедия, YouTube)

    Какие факторы на это влияют:
    • Умение веб-программиста — что он умеет, то и пишет.
    • Используемый инструментарий — скажем, у нас есть древний сайт, надо переписать под мобилу.
    • Соотношение «настольный/мобильный» — например, заказ пиццы обязательно делать преимущественно мобильным или адаптивным.
    • Контент — если много картинок, приходится думать. И наоборот, простой блог без проблем можно сделать преимущественно мобильным, что и сделал Бирман в своей Эгее.
    • Особый настольный инструментарий — например, вики смотрят и с мобилы, но редактирование с мобилы — крайне редкий сценарий и обычно ограничивается редактурой. YouTube с мобилы много глазеют, но залить с мобилы — дело значительно более редкое. И уж совсем редкое — управлять видеоблогом с мобилы.

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

    @Mercury13
    Программист на «си с крестами» и не только
    1) То есть считал строку, скомпилировал - получил двоичный код. Выполнил этот двоичный код.

    НЕТ. Считал — выполнил. Выполняется машинный код интерпретатора, а не программы.

    3) Байт-код понятен среде/виртуальной машине (.NET, Java), которая компилирует байт-код в двоичный код

    В ЧИСТОМ СЛУЧАЕ — НЕТ. Считал команду байт-кода — выполнил. Точно так же выполняется машинный код интерпретатора, а не программы. Что-то вроде:
    switch (opcode) {
    case OP_ADD: {
           auto res = stack.pop() + stack.pop();
           stack.push(res);
       }
    .....
    }


    Байт-код используется по нескольким причинам.
    1. 3 быстрее 1.
    2. С расширением языка мы меняем только компиляцию в байт-код. Оптимизация байт-кода, интерпретатор — остаются.

    А компиляция в машинный код в 1 и 3 — это так называемая JIT-компиляция. Она может выполняться не всегда. Одни команды могут быть в машинном коде, а другие — интерпретироваться.

    Кроме того, байт-код часто используется и в классических компиляторах 2-го типа! Это позволяет делать многоплатформенные коллекции компиляторов — все frontend’ы компилируют в байт-код, затем с байт-кодом делаем какую-то магию вроде оптимизации, отдаём его backend’у, и тот готовит OBJ-файл для нужного процессора.
    Ответ написан
    Комментировать
  • Почему нельзя возвращать объект по значению в non-const &?

    @Mercury13
    Программист на «си с крестами» и не только
    const struct s & ref = f();
    Этот механизм называется «продление жизни временного объекта».
    Можно написать вот так.
    struct s && ref = f();

    Главная причина преобразования временного объекта только в const T& и T&& — тонкие ошибки при суперпозиции функций.
    А именно — временный объект мы ни в коем случае не должны передать в функцию, принимающую s&.
    void modify(s& arg) { ++arg[1]; }
    
    ...
    
    modify(f());

    Мы вызвали функцию f(), её результат передали в modify, и… похѣрили.
    Ответ написан
    Комментировать
  • Для чего нужны спецификаторы в C?

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

    Именно по этой причине — спецификаторы printf в первую очередь задают типы аргументов — функция printf ничего не может сделать, если мы ошиблись с типом. Потому что %d (%ld, %lld) для неё в первую очередь способ понять, сколько байтов взять со стека и как их интерпретировать.

    Си++ для этих целей использует перегрузку функций, Delphi — тэгированные данные, Java — объектный полиморфизм. Так что они знают, с каким типом данных имеют дело — и для них не нужно различать signed/unsigned int/long/long long. Для них %d/%x/%X означает разные варианты целого, %e/f/g — разные варианты дробного, и. т.д. Если для дробного пользователь напишет %d, они или выкинут ошибку, или как-то интерпретируют.
    Ответ написан
    Комментировать
  • Можно ли спикером микротика сгенерировать голос?

    @Mercury13
    Программист на «си с крестами» и не только
    На многозадачных ОС — скорее всего, нет. Потому что в DOS мы могли напрямую управлять мембраной динамика, а для этого нужно жёсткое реальное время. Чтобы несколько тысяч раз в секунду по расписанию управление передавалось нашей программе. Кстати, под Windows все эти программы превращались в рёв реактивного двигателя, помните?

    А музыкой занимается программируемый таймер, железная микросхема. Чтобы звук был побогаче, там обычно налаживают быстрые арпеджио, единицы-десятки раз в секунду — быстродействия многозадачной ОС общего назначения вполне хватает.

    Звуковая плата управляет динамиком своими силами, через DMA, без участия процессора.
    Ответ написан
  • Возможно ли автоматическое добавление переменной компилятором в namespace?

    @Mercury13
    Программист на «си с крестами» и не только
    В любом языке можно только так.
    namespace Addresses {
      uintptr_t myAddresses = processManager.xxx;
    }

    Только в CPP-файле; в хедере надо
    namespace Addresses {
      extern uintptr_t myAddresses;
    }

    Но не советую по двум причинам.
    1. Контроль задержек при инициализации программы — дело противное. На всех языках. Например, на Java ME я просил: все длинные операции делать, когда на экране что-то появится.
    2. Вторая причина специфичная для Си++. Порядок инициализации разных модулей не определён. Если менеджер в одном модуле, а адреса в другом, адреса могут с вероятностью 50/50 инициализироваться, когда менеджера ещё нет.
    Ответ написан
    6 комментариев
  • Как найти слово, гарантированно отсутствующее в наборе?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Придумал, причём тупо. Комбинаторикой высчитываем, какой длины (по принципу Дирихле) должно быть наше слово: например, если у нас цифры от 0 до 9 и 125 строк, достаточно взять трёхзначные. Вот мы берём 126 битов (ну или 128 для круглого счёта), из наших чисел берём только трёхзначные и начинаем заполнять биты. Есть, например, 025 — вот в 25-й бит и суём единичку. После этого остаётся найти в битовой маске ноль и преобразовать в нашу строку: на 45-м месте — значит, 045.
    Ответ написан
    Комментировать
  • Как заменить одинарный обратный слеш?

    @Mercury13
    Программист на «си с крестами» и не только
    Не сильно оптимизированный, но действующий код.
    CONST_STR = string_view или const string& в зависимости от версии Си.
    На выходе — кол-во замен.
    size_t str::replace(
            std::string& aString,
            const char aWhat,
            const char aByWhat)
    {
        size_t r = 0;
        FOR_S(i, 0, aString.length())
            if (aString[i] == aWhat) {
                aString[i] = aByWhat;
                ++r;
            }
        return r;
    }
    
    size_t str::replace(
            std::string &aString,
            char aWhat,
            CONST_STR aByWhat)
    {
        if (aByWhat.length()==1)
        {   // Simple replace
            return str::replace(aString, aWhat, aByWhat[0]);
        }
        // More complex replace
        static const size_t szWhat = 1;
        const size_t szByWhat = aByWhat.length();
        size_t p = 0, r = 0;
        while ((p = aString.find(aWhat, p)) != std::string::npos)
        {
            aString.replace(p, szWhat, aByWhat);
            p += szByWhat;
            ++r;
        }
        return r;
    }
    Ответ написан
    Комментировать
  • Как в Qt (например, QTextBrowser) сделать подчёркивание тоньше пикселя?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Опять приходится отвечать самому.

    Хорошего способа нет, но самая близкая разглючка — через градиентный фон. Например.
    style = " style='color:" CNAME_LINK_POPUP "; "
                         "text-decoration:none; "
                         "background:qlineargradient(x1:0, y1:1, x2:0, y2:0, "
                                "stop:0 " CNAME_LINK_POPUP ", stop:0.06 #00000000, stop:1 #00000000);'";
    Ответ написан
    Комментировать
  • Где хранится nullptr? Для разных программ она разная?

    @Mercury13
    Программист на «си с крестами» и не только
    Что значит «хранится»?
    • Значение nullptr хранится в переменной-указателе — как и любое &someObject.
    • Объекта правильной «объектной» формы (если, конечно, у объекта присутствует какой-то инвариант или хотя бы таблица виртуальных методов) по адресу *nullptr нет; в большинстве ОС разыменование nullptr приводит к нарушению сегментации и мгновенному вылету (но не обязательно — например, DOS).
    • Во всех известных мне ABI (x86/DOS, x86 и x64/Windows, x86 и x64/Linux) nullptr равен нулевому адресу. Это обосновано — по этому адресу обычно располагается таблица прерываний, точка входа, префиксный сегмент или прочая системная хрѣнь, сюда точно соваться не стоит. Но теоретически не обязательно. Этот адрес, естественно, общий для ABI, чтобы можно было объединять в одну программу результаты компиляции разными языками.
    Ответ написан
    Комментировать
  • Возможно ли сделать шаблонную функцию дружественной классу?

    @Mercury13
    Программист на «си с крестами» и не только
    #include <iostream>
    
    class Wrap
    {
    public:
        Wrap(int x) : value(x) {}
    private:
        int value;
    
        template <class T>
        friend void out(const T&);
    };
    
    template <>
    void out(const Wrap& x)
        { std::cout << x.value << std::endl; }
    
    int main()
    {
        Wrap x(42);
        out(x);
        return 0;
    }


    Если нужно в пространстве имён…
    spoiler
    #include <iostream>
    
    namespace qq {
        template <class T>
        void out(const T&);
    }
    
    class Wrap
    {
    public:
        Wrap(int x) : value(x) {}
    private:
        int value;
    
        template <class T>
        friend void qq::out(const T&);
    };
    
    template <>
    void qq::out(const Wrap& x)
        { std::cout << x.value << std::endl; }
    
    int main()
    {
        Wrap x(42);
        qq::out(x);
        return 0;
    }
    Ответ написан
    Комментировать
  • Почему в C++ всё именно так?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Const-корректность придумал Бьярне Строуструп, и только ≈1999 её внесли в Си без крестов. А поскольку память, занятую строковым литералом, изменять запрещается (а в современных ОС литералы сидят в особом неизменяемом сегменте), понятное дело, новым типом для них стал const char*. Поскольку Си99 для совместимости позволял писать char* text = "foo";, а разработка Си(++) застопорилась на десятилетие, учебники по Си очень долго писали такую нерекомендуемую строчку.

    3. Для маленьких объектов это одно и то же, но для больших немалая разница. Эта программа компилируется начиная с Си++17.
    #include <iostream>
    
    class WrapInt
    {
    public:
        int value = 0;
        WrapInt(int x) : value(x) {}
        WrapInt(WrapInt&) = delete;
    private:
    };
    
    int main()
    {
        WrapInt x = 42;
    }


    int zero = 0; до C++14 — создать временный объект, вызвать конструктор перемещения, а остальное делает оптимизатор. С Си++17 — вызов конструктора, если он не explicit. Мне казалось, что в очень старых версиях DJGPP был задействован op=, но ХЕЗ, MinGW и Си++03 вызывают именно конструктор.

    int zero = int(0); — что удивительно, то же самое, только конструктор любой! Хотя выглядит как явное создание временного объекта.

    int zero(0); — вызов конструктора во всех версиях Си++.

    int zero{0}; — универсальный инициализатор Си++11, также вызов конструктора.

    4. int array[] = { 1, 2, 3}; — все версии Си и Си++. int array[] { 1, 2, 3}; — универсальный инициализатор Си++11.

    6. Эдсгер Дейкстра рассказал, почему массивы должны нумероваться с нуля, а целые диапазоны — задаваться в формате [a,b). Итак, у нас есть диапазон [a,b) и массив с запасом. Как сдвинуть b и добавить в диапазон один элемент? — array[b] = newElem; ++b;. Керниган и Ритчи решили объединить это чудо в одну строку array[b++] = newElem;, к тому же в процессорах действительно бывают пред- и постинкрементные операции.

    7. void — это псевдотип, чьи элементы создавать нельзя. Указатели можно передвигать с шагом, который равен размеру типа — то есть прибавляем 1, реально прибавляется 1, 2, 4, 8 или сколько там. Для void*, который является простым указателем на память, шаг 1 байт. Или 1 слово, если машина оперирует более крупными словами — такие были, но из-за переусложнённой работы с текстом от них отказались. Но инкременты-декременты void*, если и возможны, то нежелательны, и вот почему.
    Чтобы «соединить ежа с ужом» (длинное слово и доступ по байтам), x86 говорит: работа с невыровненными данными (например, 4 байта по адресу 21) будет происходить медленно и по неопределённому алгоритму. Itanium вообще, ЕМНИП, к невыровненным данным не обращается, как это устроено в компиляторах: char* → int* говорит, что обращаться нужно медленно и программно, void* → int* — быстро и аппаратно.

    8. libc можно и не подключать, хоть это и сложно. А return из main НЕ вызывает std::exit. Линкер создаёт код обвязки, который открывает все глобальные объекты, вызывает main), закрывает все глобальные объекты и выходит из программы по-системному.
    Ответ написан
    Комментировать
  • Что за скобки в выражении std::true_type{}?

    @Mercury13
    Программист на «си с крестами» и не только
    Универсальный инициализатор Си++11. В нём может быть что угодно: параметры конструктора, initializer_list, поля структуры… Ваш код эквивалентен коду
    std::string helloWorld(std::move(hello().operator std::string()));

    Вроде верно — раньше, НЯЗ, в helloWorld вызывался op= КОНСТРУКТОР КОПИРОВАНИЯ, а сейчас С СИ++17 конструктор.
    Для временных объектов вызывается версия string(string&&), я это обозначил как string(std::move()).
    Ну и для преобразования hello в string используется hello::op string.
    hello(), или, начиная с Си++11, hello{} — это создание временного объекта с помощью конструктора по умлочанию.
    Ответ написан
    Комментировать