• Как именно гарантируется выделения n байт памяти библиотекой stdint.h?

    @res2001
    Developer, ex-admin
    В Си, да и в С++, стандартные целочисленные типы, поддерживаемые компилятором - это char, short, int, long long и их беззнаковые братья. Стандарт действительно не фиксирует размеры стандартных целочисленных типов. Это сделано потому что стандарт описывает абстрактный язык, который должен компилироваться для разных платформ.

    Компилятор же компилирует программу под конкретную платформу с конкретными соглашениями по типам. Ему заведомо известны размеры стандартных типов на данной конкретной платформе. Стандартная библиотека так же обычно пишется под конкретный компилятор и конкретную платформу.
    Так что (u)intX_t - это всегда define над стандартными типами. И ничего странного в этом нет.
    Ответ написан
    Комментировать
  • Что такое культура программирования?

    xez
    @xez
    TL Junior Roo
    Почти то же самое, что культура коммуникации.
    - Следование код-стайлу. Адекватный нейминг.
    - Умение писать выразительный, понятный код. Без лапши, без убер-методов на миллион строк, без олимпиадных трюков (если нет такой необходимости); код, в котором можно будет легко разобраться.
    - Любовь к тестам. Понимание почему и зачем тесты писать и почему и зачем не писать.
    - Следование общепринятым инженерным практикам. В Гите не пушить без реквеста в мастер, не ребейсить без необходимости, подписывать комиты, описывать реквесты. В бд предпочитать миграции и не лазить в прод трясущимися, грязными ручонками. Релизы согласовывать, к стейджам относится уважительно. В очереди не срать. Рута избегать.
    - Скилл в декомпозиции и оценке. Умение прогнозировать разработку, умение давать обещания и умение их выполнять.
    - Отсутствие магического мышления. Понимание причино-следственных связей. Знание что такое логи и что такое метрики.
    - и т.д..
    Ответ написан
    Комментировать
  • Что быстрее индексы или указатели?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Зависит от модели процессора, версии и опций компилятора и немножечко фазы луны. В целом без разницы.

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

    Совет по бенчмарку - если памяти не хватает, стоит по одному достаточно большому массиву пройтись 10000 раз. А лучше использовать готовые фреймворки для измерения скорости, вроде того де gbenchmark.

    Еще, иногда полезно посмотреть на ассемблерный выхлоп. Вот, например, что происходит при -O3 опции компилятора. Он генерирует вообще идентичный код для обеих функций (развернув циклы)! И даже при -O2 оно одинаковый код выдает.

    Без оптимизаций код разный, но там все не так как вы думаете. Вместо инструкции mov eax, dword ptr [rax + 4*rcx] в варианте с индексами используется инструкция mov eax, dword ptr [rax] для указателей. Это самое "складывание с указателем массива" вообще не отдельная операция - а вариант адрессации в инструкции mov. Они могут вообще одинаковое количество тактов занимать, это надо мануал по конкретной архитектуре процессоров читать.
    Ответ написан
    Комментировать
  • Почему перемещение объявления и инициализации переменной на новую строчку кода влияет на результат работы программы?

    shurshur
    @shurshur
    Сисадмин, просто сисадмин...
    В этом коде есть важная ошибка. В циклах for нигде не задано начальное значение переменной i, поэтому она может иметь любое значение, её поведение неопределено. Например, она может выделиться там же, где была выделена предыдущая, поэтому i во втором цикле будет равна последнему значению в предыдущем, то есть 10, даже если в первом цикле повезло попасть на 0.

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

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

    Решение простое: надо везде в циклах for указать начальное значение i, тогда всё станет нормально, и перестановка определения count перестанет создавать такие совсем не странные эффекты.
    Ответ написан
    3 комментария
  • Как хранится struct в памяти?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    Зависит от компилятора и заданных при компиляции опций. Например, при плотной упаковке (#pragma pack(1)) каждый элемент структуры занимает ровно столько, сколько ему необходимо. А при выравнивании на 64 бита (#pragma pack(8)) под каждый элемент выделится память, кратная 8 байтам и достаточная для размещения элемента. Для разных архитектур процессоров могут быть доступны разные настройки выравнивания.
    Ответ написан
    Комментировать
  • Издержки полиморфизма или неправильный дизайн?

    vabka
    @vabka
    Токсичный шарпист
    1. Не нужно пихать getChar и getFloat в абстрактный класс.
    Но если таки делаешь заглушки - кидай полноценную ошибку, чтобы гарантировать корректное использование.

    2. Вместо этого можно было бы использовать паттерн visitor - по крайней мере его часто используют в ситуациях, которые похожи на твою

    3. Вместо указателей на классы можно попробовать union - для этого есть std::variant. Это будет чуть более эффективно по памяти, хоть размер массива может вырасти, если вместо float будет double, а вместо char - какой-нибудь wchar или "руна", или если добавится ещё какой-нибудь тяжёлый вариант. А до этого такой Юнион можно уместить в 8 байт вместе с выравниванием.
    Ответ написан
    1 комментарий
  • Устарел ли учебник Стивена Прата по C++?

    @res2001
    Developer, ex-admin
    Знаю только одну книгу на русском по С++20: https://dmkpress.com/catalog/computer/programming/... Хотя пристально вопрос не отслеживаю, возможно появилось что-то еще в других изданиях. Эта книга совсем не учебник - вы не научитесь по ней программированию на С++.
    Тем более вы не найдете учебник, где бы был описан 20 стандарт. Возможно на английском есть.
    Вообще книги формата учебников подтягиваются к современным стандартам с большим запозданием - лет 5 это норма. В любом случае Прата подойдет для изучения языка. После него можно углубить изучение каких-то вопросов, по которым остались пробелы и изучить нововведения поздних стандартов.
    Ответ написан
    Комментировать
  • Как оформить код?

    @dima20155
    you don't choose c++. It chooses you
    самое простое - используйте typedef или using
    А также можно написать свой класс-обертку для удобства.
    Ответ написан
    3 комментария
  • Правильно ли я понимаю правила arithmetic conversions?

    @dima20155
    you don't choose c++. It chooses you
    Да, тип будет именно таким.
    Чтобы самостоятельно увидеть вывод типа, который вывел компилятор Scott Meyers предлагает намеренно создать ошибку конвертации типа и посмотреть что же компилятор написал.
    https://godbolt.org/z/6bobqTG58
    Ответ написан
    1 комментарий
  • Почему tellg() неявно приводится к int при инициализации int, но не может быть сложенным с int?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Очевидно, потому что возвращаемый тип имеет определенный оператор преобразования к int или long, но не имеет переопределенного operator+ с int.

    Если смотреть описание возвращаемого типа, то там есть operator+ с каким-то streamoff, а с int - ничего нет. Там, правда, не указано, что есть опретор преобразования к int, так что это, наверно, тоже лучше не использовать.
    Ответ написан
    Комментировать
  • Как извлечь элементы многобайтового массива как единое число?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Лучше так делать не надо. Это UB - нарушение всяких strict aliasing, выравниваия и вообще от порядка байт в машине зависит. Лучше руками собрать ULL по частям, вроде
    for (int i = 0; i < 8; ++i) result |= byte_array[i+1] << (8ULL*i);
    или
    for (int i = 0; i < 8; ++i) result |= byte_array[i+1] << (8ULL*(7-i));


    На худой конец, если очень узкое место, надо делать memcpy из массива в &result.
    Ответ написан
    Комментировать
  • Уменьшается ли используемая память программы?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Не гарантированно, но в некоторых случаев компилятор действительно сможет переиспользовать место на стеке под переменную a для какой-то новой локальной переменной, когда a выйдет из зоны видимости. Но чаще это место просто будет пустым до конца функции и никакой экономии памяти вы не получите.

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

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

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    struct message{
        int id;
        char* data;
    };
    …
    send(fds[i].fd, &msg, sizeof(msg), 0)

    Этот send отправляет клиенту не данные, а указатель. Указатель на данные, которых у клиента нет.

    // Add new socket to poll array
                fds[nfds].fd = new_socket;
                fds[nfds].events = POLLIN;
                nfds++;
            }
     
            // Check for data from clients
            for (int i = 1; i < nfds; i++) {
                if (fds[i].revents & POLLIN) {


    Здесь ты добавил сокет в массив дескрипторов полл и сразу проверяешь, не установлен ли у него revents. Но это поле в этот момент не инициализировано. Мало того, ты просишь ожидать POLLIN, но клиент никогда ничего не отправляет серверу, поэтому и сервер не дождавшись POLLIN никогда ничего не отправляет клиенту.

    struct pollfd fds[1];
        fds[0].fd = sock;
        fds[0].events = POLLOUT;
        if (poll(fds, 1, -1) <= 0) {
            perror("poll failed");
            exit(EXIT_FAILURE);
        }
        if (!(fds[0].revents & POLLOUT)) {
            perror("connect failed");
            exit(EXIT_FAILURE);
        }
    
        // Receive message from server
        while ((valread = read(sock, &msg, sizeof(msg))) == -1 && errno == EAGAIN);


    Здесь ты ждёшь до POLLOUT, но после этого начинаешь читать. Это малость нелогично, потому что наличие данных для чтения показывается флагом POLLIN. POLLOUT же на свежеустановленном соединении есть сразу, поэтому чтение тупо вертится в цикле while пока не прийдут данные.

    как это можно попробовать подебажить понять что не так, что происходит

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

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Основным постулатом работы с шаблонами является то, что определение шаблона должно быть достижимо из места его инстанцирования.

    Что это означает на практике. После 4й стадии трансляции формируется модуль трансляции, содержимое которого и компилируется в объектный файл. Код модуля трансляции должен быть исчерпывающим, в нем должны находиться все необходимые для компиляции определения. А определение шаблона относится именно к таким требуемым.
    Значит определение используемого в модуле шаблона должно быть внесено в модуль трансляции. А это значит что или определение должно быть вписано в тот .cpp файл, в котором шаблон инстанцируется, или определение должно быть добавлено через директиву #include.

    В первом случае, когда используемое в .cpp файле определение шаблона вписано в том же .cpp файле, определение шаблона будет достижимо только в данном .cpp файле и больше нигде. В этом случае попытка инстанцировать шаблон в другом модуле трансляции приведет к ошибке трансляции, т.к. там определние шаблона будет уже недостижимо.
    Файлы исходного кода включать через директиву #include - это дурной тон и прямой путь к нарушению ODR.

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

    Широкой практикой является разделение кода шаблонов на несколько типов файлов. В .h файлах обычно делают или объявления шаблонов функций, или определения шаблонов классов. В .hpp/.inl файлах обычно делают определения шаблонов функций и шаблонов методов.
    При этом .hpp/.inl файл очень часто включается в самом низу .h файла с его объявлениями.
    Моя личная рекомендация: использовать в таких случаях расширение .inl (от слова inline), т.к. для .hpp столь же широко закреплено взаимоисключающее с .h значение заголовка C++ кода. И видеть эти два расширения в одном проекте обычно бывает странно.

    Вектор же, например в проекте LLVM, реализован так, что часть определений в нем сделаны по месту объявления, а часть - как внешние определения сразу после определения шаблона вектора. Все это сделано прямо в одном заголовке.
    Ответ написан
    Комментировать
  • Почему вылазит link error(не видит вирутальные методы?)?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Потому что шаблон.

    Надо или весь класс определять в хедере, или в array.cpp указывать компилятору генерировать инстанс шаблона с параметром, который нужен в другом cpp файле:
    using Array<int>;

    То же и для очереди.

    Происходит это потому, что компилятор генерирует шаблоны лениво - только когда они нужны. Вот, компилируя array.cpp он не видет вообще ни одного использования шаблона и не генерирует ничего. В каком-нибудь main.cpp у него есть объявление из array.h и использование шаблона. Он и генерирует объявления методов. Но определения-то нигде нет. Оно в array.obj должно быть по вашему замыслу, а там пусто.
    Ответ написан
    1 комментарий
  • Почему возникает free(): double free detected in tcache 2? (в деструкторе)?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Почему возникает free(): double free detected

    Потому что в этом классе не реализован конструктор копирования (а так же перемещения и операторы присваивания, но это пока не вызывает таких же ошибок). В результате при копировании объекта класса String копия получает то же значение str что и оригинал с которого она скопирована, в деструкторе копия удаляет str оригинала, а потом это же делает оригинал в своём деструкторе. См. правило трёх/пяти.
    Ответ написан
    Комментировать
  • Как скрыть адрес вызываемой функции в C++?

    @rPman
    В коде, указанном в вопросе написана белиберда
    В конструкторе предлагаешь сделать присваивание
    vProtect = Protect;
    глобальной переменной vProtect имя класса Protect (потому что у тебя Protect и класс и переменная типа cProtect, (то что компилятор тебе это позволил уже бардак), если переименовать cProtect мембер Protect в protect (как рекомендует большинство code styling - имена классов с большой буквы, имена переменных - с маленькой, чтобы не запутаться), а еще тип vProtect у тебя - указатель, а Protect - нет, если будут оба указатели то само собой все будет собираться, но все равно смысла это иметь не будет.
    -----------------------

    Теперь про твой вопрос в заголовке, почти ничего не имеющий общего с текстом вопроса:
    Как скрыть адрес вызываемой функции в C++?
    в контексте обфускации, подразумевается что по декомпилированному исходному коду должно быть не ясно, какой именно метод будет вызываться (т.е. для анализа требуется отладка, что сложнее/дороже), значит хранить адреса методов нужно в каких то переменных, например массивах, а выбор следующего вызываемого метода делать на основе каких то вычислений по коду.

    В c++ для этого реализован класс std::function, пример использования для вызова именно метода класса (для простоты пример без аргументов но с аргументами все то же самое, надеюсь ты понимаешь, что у тебя должны быть одинаковые аргументы и типы во всех методах, или должны быть группы для разных типов, но чем больше групп тем проще анализ кода, иначе тупо по типам и количеству аргументов все можно будет понять)
    // определяем класс
    class MyClass
    {
      public:
      void myFunA(){std::cout<<"A";};
      void myFunB(){std::cout<<"B";};
    };

    однократно где то инициализируешь массив адресов функций (никто не мешает по коду это перемешивать)
    std::function<void(MyClass*)> functions[]={&MyClass::myFunA,&MyClass::myFunB};

    вызов метода по номеру x
    MyClass obj;
    functions[x](&obj);
    Ответ написан
    3 комментария
  • Какую роль играют float и double в скобках?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Такая запись имени типа в скобках в выражении - это явное приведение типа. Тут вы указываете компилятору, что выражение надо преобразовать вот к такому вот типу (флоат, в данном случае).

    float нужен вам в начале, потому что вещественные константы имеют тип double. Поэтому у eps/2.0 и 1.0 в первом цикле имеют тип double, все вычесляется в double. Преобразовав одно из выражений в float вы получаете то, что вам надо. Без этого все вычисления идут в double и ответ находится не тот. На самом деле там float при сравнении все-равно расширяется до double но на результат сравнения это не влияет в данном случае.

    Еще, вместо явного приведения типов, можно поставить f после вещественных констант, чтобы указать компилятору, что это float:
    while (1 + eps/2.0f != 1.0f){

    Тогда вычисления будут во float и ответ будет правильный.

    Во втором цикле double вам не нужен. Ведь вычисления итак идут в этом типе. Да и написано у вас там с опечаткой, вы к типу double приводите все выражение со сравнением. т.е. вы булево значение преобразуете в дабл. Потом оно назад в булево преобразуется при проверке условия циклом. В итоге это бесполезное действие.
    Ответ написан
    Комментировать
  • Какую роль играют float и double в скобках?

    Adamos
    @Adamos
    Первый float - это приведение выражения за ним к типу float.
    Вообще говоря, ненужное, поскольку сложение int и float и без этого приведения будет float.

    Второй double - демонстрация того, что писавший вообще не понимает, что делает.
    Ибо тут к вещественному типу приводится... булев результат сравнения.
    Ответ написан
    8 комментариев
  • Каковы правила конвертации указателя на массив неопределенной длины в указатель на массив определенной длины?

    Adamos
    @Adamos
    С++ - язык более высокого уровня, тут компилятор не позволит вам выстрелить себе в ногу неявным приведением сена к соломе. Нужно объявить его явно.
    static_cast - если компилятор готов согласиться с вами, что одно можно просто привести к другому во время компиляции.
    dynamic_cast - если вы приводите указатель на родительский класс к указателю на дочерний
    и reinterpret_cast - если вы приводите по-сишному, "ногой в дверь", уверены в себе и не нуждаетесь в подсказках компилятора насчет того, что делаете.
    Ответ написан
    4 комментария