Ответы пользователя по тегу C
  • Как сделать такой финт ушами с double?

    @Mercury13
    Программист на «си с крестами» и не только
    Почему нельзя? Пишу на Си с крестами, но там ничего специфичного нет, кроме парочки перестраховок.
    В Си++20 появился ещё и bit_cast.

    #include <iostream>
    
    union DoubleInt {
        double asDouble;
        uint64_t asInt;
    };
    
    static_assert(sizeof(double) == sizeof(uint64_t), "Strange machine with double != int64");
    
    constexpr int BITS_MANTISSA = 52;
    constexpr int BITS_EXPONENT = 11;
    constexpr int BITS_SIGN = 1;
    
    static_assert(BITS_MANTISSA + BITS_EXPONENT + BITS_SIGN == 64, "Programmer's funkup");
    
    constexpr uint64_t MANTISSA_UNIT = uint64_t(1) << BITS_MANTISSA;
    constexpr uint64_t MANTISSA_MASK = MANTISSA_UNIT - 1;
    
    constexpr int EXPONENT_SHIFT = BITS_MANTISSA;
    constexpr uint64_t EXPONENT_MAX = (uint64_t(1) << BITS_EXPONENT) - 1;
    constexpr uint64_t EXPONENT_ORIGIN = EXPONENT_MAX >> 1;
    constexpr uint64_t EXPONENT_MASK = EXPONENT_MAX << EXPONENT_SHIFT;
    constexpr uint64_t EXPONENT_SHIFTED_ORIGIN = EXPONENT_ORIGIN << EXPONENT_SHIFT;
    
    constexpr int SIGN_SHIFT = BITS_MANTISSA + BITS_EXPONENT;
    constexpr uint64_t SIGN_MASK = uint64_t(1) << SIGN_SHIFT;
    
    int main()
    {
        DoubleInt x { -3.45 };
    
        // Простите уж, без денормализованных чисел
    
        // Оставим знак и мантиссу
        DoubleInt xMantissa = x;
        xMantissa.asInt &= (MANTISSA_MASK | SIGN_MASK);
        // И добавим туда стандартный нулевой порядок
        xMantissa.asInt |= EXPONENT_SHIFTED_ORIGIN;
    
        // Извлечём порядок
        int exponent = ((x.asInt & EXPONENT_MASK) >> EXPONENT_SHIFT) - EXPONENT_ORIGIN;
    
        std::cout << xMantissa.asDouble << "*2^" << exponent << std::endl;
    
        return 0;
    }
    Ответ написан
    1 комментарий
  • Почему 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
    Программист на «си с крестами» и не только
    не удается преобразовать '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.

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

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

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

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

    @Mercury13
    Программист на «си с крестами» и не только
    Это чья-то собственная. Вы её и не найдёте. Особенно вместе с общими названиями generate и foo.
    Шерстите свой исходник. А если он не полный — вы сами себе злобный буратино.
    Ответ написан
    Комментировать
  • Почему при выводе элемента из перечисления выводит не нужное мне значение?

    @Mercury13
    Программист на «си с крестами» и не только
    Полагаю, дело в системе сборки — нечто подобное я испытывал и в Doom (старом, 1994 года), где makefile редактировался руками.

    Например, у вас было
    typedef enum Buttons{
        SAVE,
        OPEN,
        SORT,
        EXIT
    }Buttons;

    После того, как вы откорректировали enum, один файл перекомпилировался, другой нет — отсюда такой артефакт.
    Ответ написан
  • Почему при вводе текста добавляются лишние символы?

    @Mercury13
    Программист на «си с крестами» и не только
    Я сделал вот такой код (простите, для простоты «с крестами»).
    #include <stdio.h>
    #include <iostream>
    
    int main() {
        int a = getchar();
    
        while (a != '\n') {
            std::cout << a << "-" << static_cast<char>(a) << std::endl;
            a = getchar();
        }
    }
    
    143-П
    224-р
    168-и
    162-в
    165-е
    226-т

    Так что не здесь полом. Но учтите, что работа была под виндой, а значит, в кодовой странице DOS-866. Подкиньте ОС, рабочую кодировку и чуть больше кода обвязки.

    UPD1. Если символов 12 вместо 6 и ОС Android — перед нами кодировка UTF-8. И выводить в консоль по одному символу не очень кузяво, поскольку для русского текста получаются неполные кодовые позиции. Закройте буфер нулём и выведите целиком.
    Ответ написан
  • Почему прошивки пишут на С?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что низкоуровневый софт должен…
    • Быстро выполняться. Потому что его выполняют или в глубоких циклах (например, ОС), или на слабом железе.
    • Расходовать мало памяти и не «течь». Потому что его часто выполняют на слабом железе. Или в чужих стеках, как драйвер.
    • Содержать мало зависимостей. Если мы зависим от большой библиотеки вроде Qt, а её реализации на данной машине нет — выкуси. Точно так же интерпретатор Питона может оказаться лишней зависимостью. И многопоточка, которая часто требуется для «мусорщика».
    • Быть совместимым с кодом на других языках. Это касается системного и драйверного кода, который вызывают из прикладного ПО (или, наоборот, прикладной кода из системной проги) — и даже с кодом на разных версиях .NET в расширениях оболочки Windows есть вопрос.
    • И в то же время требуется некая доля переносимости и абстракции. Например, мы пишем джойстик на AtMega и не хотим мучиться с длинными числами — ЯВУ лучше будет, чем ассемблер. 10 бит АЦП на 8-битном процессоре уже длинное число!!

    Почему Си? У него есть две фишки: большое поле для ручной оптимизации (ключевое слово register, op++), и он полагается на две ассемблерных утилиты — линкер и библиотекарь (tlink и tlib, например). Из-за этого компилятор Си довольно просто написать под новую машину, и на НЕоптимизирующем компиляторе можно писать довольно быстрый код.
    Ответ написан
    Комментировать
  • Что не так в Eclipse IDE?

    @Mercury13
    Программист на «си с крестами» и не только
    printf("%c", x);
    Форматную строку упустил.
    Ответ написан
    1 комментарий
  • Можно ли удалить пробелы в начале и в конце строки без создания нового массива?

    @Mercury13
    Программист на «си с крестами» и не только
    Строковый литерал " ab " находится в особом сегменте данных, который (если процессор и ОС позволяют) только для чтения. А память сегмента данных освободить и сделать «кучей» нельзя.

    Если предположить, что текст " ab " находится в «куче» — эта самая куча является сложной структурой данных и память выделяется с выравниванием. Потому функция realloc может (но не обязана) отдать конец или приделать память к концу. Отдать начало будет сложнее, и ни одна известная мне библиотека функций на это не способна.

    Кроме того, Си++ придумал объект string_view именно для этого — чтобы передавать строки в функцию, абстрагируясь от выделения памяти и даже от оконечного нуля. Потому у string_view нет функции c_str().

    Разумеется, вы можете сделать что-то вроде
    char* str2 = str + 2;
    str2[2] = '\0';
    // А str оставь, уничтожать как-то придётся

    Разумеется, если str находится в куче. Сегмент с литералами, напоминаю, только для чтения, и в ОС вроде DOS, где менять можно, такая замена может вызвать нехорошие побочки.
    Ответ написан
    3 комментария
  • Не могу понять почему пишет функция перегружаная 1 ошибка?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут причины не видно, она за правой границей экрана. Вероятнее всего, вы открыли или закрыли лишнюю скобку, из-за чего sqrt стала с двумя аргументами.
    Ответ написан
  • Возможно ли в процедурном программирование, имитировать ООП?

    @Mercury13
    Программист на «си с крестами» и не только
    Можно, и я бы посоветовал посмотреть на две вещи.
    1. MiniZip. Настоящие виртуальные потоки, написанные на чистом Си!
    Кстати, у нас на работе есть библиотека MiniZipPort, представляющая собой тупой перевод MiniZip на виртуальные потоки и шаблонные контейнеры — две строчки Си превращались в одну строчку на «крестах». К тому же для хранения каталога я использовал уже имевшийся самописный AccuStream (что-то вроде std::deque, только в виде потока).
    2. Doom. Самый первый, 1993–94 года. Там тоже ухитрились делать объектно — правда, другими методами.
    Ответ написан
    3 комментария
  • Как прочесть аргументы функции на стеке / заставить gcc хранить аргументы функции на стеке?

    @Mercury13
    Программист на «си с крестами» и не только
    https://godbolt.org/z/ARYhis
    Если посмотреть в пролог функции, она делает локальную копию нашей a. А желаемое место в стеке на 36 байтов выше, чем вы ищете.
    Разумеется, всё под x86. Вызов под x64 подразумевает регистры.

    UPD. А вот Интел не делает локальных копий и всё на месте.
    Ответ написан
    Комментировать
  • Ошибка при чтении из файла в си.Что делать?

    @Mercury13
    Программист на «си с крестами» и не только
    feof возвращает true, когда мы попытались прочитать и не смогли, потому что каретка ударилась в конец файла. А не когда каретка аккуратно припарковалась в конце файла — и уж тем более не когда она отделена от конца пробелами.

    Лучше прочитайте про scanf…
    On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.

    …и посмотрите, как можно переделать условие цикла.
    Ответ написан
    Комментировать
  • Не работает функция. Что сделать?

    @Mercury13
    Программист на «си с крестами» и не только
    Я пока вижу…
    shitf_up(s,Parent);

    Может, ещё вы напутали с порядком кучи, но на таком кусочке кода этого не видно.
    Ответ написан
  • В чем ошибка?Обьясните в чем ошибка?

    @Mercury13
    Программист на «си с крестами» и не только
    Вместо знака умножения плюс стоит.
    beforeTax = tirePrice + numTires;

    .07 — это очень некузявая запись для 0.07, ставки налога. Знаете же, что в США налоги зависят от кучи параметров, и их надо прибавлять вручную.
    Ответ написан
    Комментировать
  • Почему pow возвращает разные значения от одинаковых float значений?

    @Mercury13
    Программист на «си с крестами» и не только
    У меня на свежайшем MinGW (Си, режим C99) не получилось повторить, а погрешность на пределе double — так что подозрение на runtime-библиотеку и управляющее слово сопроцессора (x87 control word).
    Ответ написан
    Комментировать
  • Почему void ** можно инициализировать только void *?

    @Mercury13
    Программист на «си с крестами» и не только
    К.О.: Потому что void* — это не void. Первый — это вполне конкретный тип, второй — «псевдотип», используемый в двух контекстах.
    Ответ написан
    Комментировать
  • Как вывести массив строк в Си?

    @Mercury13
    Программист на «си с крестами» и не только
    Ваши ошибки.
    1. Вы считываете строку куда попало. Надо выделить память, или сделать статический массив char s[3][40];
    2. Обозначение для символа перевода строки — \n, с обратным слэшом.
    3. *a = a[0].
    Ответ написан
    2 комментария
  • На каких правилах основана шестнадцатеричная нотация в printf для вещественных чисел?

    @Mercury13
    Программист на «си с крестами» и не только
    Что собой представляет 17? Это 100012, или 1,00012·24, или 1000,10002·21. Теперь видите, откуда две восьмёрки и порядок 1? Правильно не 0x1.1p+1, а 0x1.1p+4.

    На основании каких правил — не могу сказать, думаю, какая-то оптимизация. Подождите, скачаю Qt, выяснилось, что на домашнем компе не осталось ничего прогерского.

    Моя первая догадка. Если исходное число было float, то первая цифра 8…F тупо проще: отрезаешь порядок и знак, добавляешь неявную единицу, и получаем 24 бита, шесть шестнадцатеричных цифр.

    Действительно библиотека MinGW выводит первую цифру 8…F, что для float, что для double. Зачем, не знаю.
    Ответ написан
    Комментировать