Ответы пользователя по тегу C++
  • Как происходит динамическое выделение памяти в С++?

    @res2001
    Developer, ex-admin
    Что подразумевается под динамикой?

    Выделение памяти в куче.

    Память может быть выделена:
    1. на стеке - автоматическая память
    2. в сегментах bss, data, rodata - статическая
    3. в куче - динамическая

    С точки зрения памяти - это все просто память процесса (некоторые диапазоны адресов в доступном адресном пространстве процесса). Но выделяется и освобождается она в разное время жизни процесса.

    Стек выделяется при старте потока (в любом процессе есть хотя бы один поток выполнения), имеет фиксированный
    размер. Освобождается стек при завершении потока. Фактически при объявлении переменной на стеке происходит просто увеличение (уменьшение) указателя стека на размер переменной, при удалении - обратный процесс. Поэтому выделение памяти на стеке очень быстрое. Переменные "живут" на стеке пока действует их область видимости, после выхода из области видимости - уничтожаются.

    Статические сегменты памяти выделяются и инициализируются при загрузке исполняемого файла программы в память еще до старта любого кода программы. Сегменты предназначены для хранения статических инициализированных/не инициализированных переменных и констант. Фактически описание этих сегментов содержится в исполняемом файле и подготавливается компилятором. Загрузчик ОС, читает эти сегменты из исполняемого файла в память. Названия сегментов (bss, data, rodata) имеют смысл только для исполняемого файла, после загрузки сегментов в память есть только адреса начала сегментов и их размер (и другие свойства страниц памяти сегментов).

    Куча. Память в куче выделяется по явному запросу программы (new) и уничтожается так же (delete). Не смотря на всю простоту интерфейса выделения/освобождения памяти операции эти довольно затратны (по процессорному времени) и под капотом может происходить много телодвижений, поэтому не следует выделять/освобождать память в куче на каждый int или еще хуже char. Для большинства программ эти затраты не критичны, но в некоторых случаях могут стать узким местом вашей программы.
    Ответ написан
    1 комментарий
  • Не работает case в switch. Как решить проблему?

    @res2001
    Developer, ex-admin
    Блоки case в операторе switch это просто аналог меток для goto. Соответственно после перехода на какой-то case выполнение программы продолжается по всем строкам кода подряд. И если вы не предусмотрели явный выход из блока case (с помощью break или return), то выполнение продолжится и в следующем case и т.д.

    Современные компиляторы умеют выдавать ошибки или предупреждения для таких случаев, т.к. очень часто в коде действительно присутствует ошибка - пропуск оператора break или return (как у вас). В gcc для этого служит опция -Wimplicit-fallthrough. Иногда проваливание в следующий case бывает полезно и используется на практике программистами, тогда (при использовании опции -Wimplicit-fallthrough) надо явно указать на это компилятору. В документации gcc в описании этой опции указано как это сделать.

    Вообще рекомендую ужесточать проверки компилятора, как минимум с помощью стандартных опций: -Wall -Wextra.
    А так же указывать какому стандарту языка надо придерживаться компилятору: -std=c++17 или -std=gnuc++17
    Можно добавить и опцию: -pedantic
    По умолчанию многие полезные предупреждения отключены.
    Многие предупреждения можно перевести в ошибку или игнорировать.
    Есть и другие полезные проверки, которые может делать компилятор, которые не входят в -Wall -Wextra. Но для начала используйте хотя бы их.

    Описание опций предупреждений gcc смотри тут: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
    Ответ написан
    Комментировать
  • Как хранятся многомерные массивы в памяти?

    @res2001
    Developer, ex-admin
    Многомерные массивы можно задавать несколькими способами:
    int arr1[2][3];
    int (*arr2)[3] = new int[2][3];
    int *arr3 = new int[2*3];
    int **arr4 = new int*[2];
    arr4[0] = new int[3];
    arr4[1] = new int[3];
    
    // Следующие утверждения верны:
    static_assert(sizeof(arr1) == (sizeof(int)*2*3));
    static_assert(sizeof(arr2) == sizeof(int(*)[3]));
    static_assert(sizeof(arr3) == sizeof(int*));
    static_assert(sizeof(arr4) == sizeof(int**));
    
    static_assert(sizeof(arr1[0]) == (sizeof(int)*3));
    static_assert(sizeof(arr2[0]) == (sizeof(int)*3));
    static_assert(sizeof(arr3[0]) == sizeof(int));
    static_assert(sizeof(arr4[0]) == sizeof(int*));

    В 1-3 варианте данные массива хранятся последовательно друг за другом по строкам. 4 вариант - это "массив массивов" (его могут называть и по другому), тут требуется отдельный массив указателей, данные строк могут хранится в разных местах памяти.
    Обращаться к элементам arr1, arr2 и arr4 можно с помощью индексации: arr1[i][j] и это будет работать, но по разному для arr1/2 и arr4.
    arr3 это по сути одномерный массив. То что он двумерный знаете только вы как программист. Поэтому через индексацию можно обращаться только к первой размерности. Чтоб включить и вторую размерность потребуется ее явно посчитать. Например для элемента arr2[1][2]:
    *(arr2 + 1 * 3 + 2);
    // или то же самое
    arr2[1*3 + 2];


    С точки зрения производительности 4 вариант самый плохой, т.к. для обращения к элементу требуется два чтения памяти, а так же из-за того что каждая строка хранится отдельно от других в памяти, то кэш процессора будет использоваться менее эффективно, чем для остальных вариантов. Кроме того тут используется дополнительный массив указателей (первая размерность).
    Не смотря на то что 3 вариант кажется довольно многословным (особенно если константы заменить на осмысленные имена переменных), но на производительности это никак не сказывается, т.к. по сути те же самые вычисления индекса делаются и для arr1/2, только для них это делает компилятор сам неявно.
    Ответ написан
    Комментировать
  • Как решить проблему с Segmentation fault?

    @res2001
    Developer, ex-admin
    Ошибка выполнения stat для файла: No such file or directory

    Потому что:
    strcpy(filename, "mntEntry->mnt_dir");
    Вы записываете в filename строку "mntEntry->mnt_dir", а не каталог.
    Добавить затем слэш в конец можно так: strcat(filename, "/");
    Или вручную:
    size_t len = strlen(filename);
    filename[len] = '/';
    filename[len + 1] = '\0';

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

    Есть еще момент. В линукс есть константа PATH_MAX она задает максимальный размер пути к файлу. Рекомендую ваши буфера для хранения путей делать размером PATH_MAX.
    Ответ написан
  • Что означает запись?

    @res2001
    Developer, ex-admin
    Похоже, это прям низкий уровень - работа с регистрами UART.
    COMBase - это базовый адрес порта. По этому адресу находятся регистры порта их несколько. Добавляя число вы пишите данные в регистр, соответствующий заданному смещению относительно базового адреса.
    Вот тут есть старинное хорошее описание низкоуровневой работы с UART и последовательным портом, с регистрами и прочим.
    Ответ написан
    Комментировать
  • Как работают std::mutex?

    @res2001
    Developer, ex-admin
    Энтони Уильямс: C++. Практика многопоточного программирования
    https://en.cppreference.com/w/cpp/thread

    Вы не блокируете тред, вы блокируете мьютекс. Если мьютекс уже заблокирован другим потоком, то второй тред будет ждать пока освободится мьютекс и потом его заблокирует. Ожидание будет происходить непосредственно в методе lock мьютекса. Есть вариант try_lock - когда ожидания не происходит, но если мьютекс уже захвачен кем-то, то возвращается соответствующая ошибка.
    В общем случае применение мьютекса выглядит так:
    m.lock()
    <unsafe operation>
    m.unlock()


    Конструкторы можно не удалять, просто в них нужно блокировать мьютекс объекта источника. Мьютекс объекта назначения блокировать не нужно, т.к. это конструктор и класс только создается и им пока еще никто не владеет, так что доступ к членам класса будет монопольный. Но в этих конструкторах надо создавать новые мьютексы для объекта назначения.
    Ответ написан
    1 комментарий
  • Устарел ли учебник Стивена Прата по C++?

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

    @res2001
    Developer, ex-admin
    Модуль B использует данные из Модуля A

    Что за данные?

    смаке оперирует целями и зависимостями, а не данными.
    Зависимости можно указать с помощью add_dependencies, подключить библиотеку с помощью target_link_libraries.
    Если у вас в каком-то из модулей генерируются какие-то данные, то, видимо, там есть custom_target - укажите его в зависимостях вышележащего уровня.
    Ответ написан
    9 комментариев
  • Как оформить код?

    @res2001
    Developer, ex-admin
    Это же трехмерный "кубический" массив. Нет смысла делать такую сложную конструкцию.
    Делайте std::vector<int> values(size * size * size);
    Правда индексы придется вычислять ручками или можно оформить это в виде своего класса и переопределить операцию индексации.
    Выделять память одним большим куском для многомерных массивов - это правильно.
    Ответ написан
    Комментировать
  • Есть ли способ получать предупреждение при преобразовании char в int?

    @res2001
    Developer, ex-admin
    По стандарту sizeof(char) == 1, sizeof(int) >= 2.
    char->int и int->char - это 2 разных преобразования. Судя по вопросу для вас это одно и то же.
    Первое проходит без предупреждения, поскольку значимость не теряется. На второе компилятор ругнется, обычно это warning, опциями компилятора можно сделать чтоб был error.
    Повышение char до int не зависимо от того какой конкретно char в данной системе вполне стандартизовано и укладывается в стандартное расширение целочисленных типов.
    Обратное преобразование приведет к обрезанию значащих бит, это то же описано в стандарте.
    Ответ написан
    2 комментария
  • Где можно почитать/посмотреть о написании dll на c++?

    @res2001
    Developer, ex-admin
    WinAPI учить не надо - бессмысленное занятие - он достаточно большой и весь он вам вообще вряд ли когда-нибудь пригодится. Но надо знать где искать по нему информацию.
    По написанию DLL на С++ ... погуглите в конце концов. Там есть свои тонкие моменты, но это не сложно.
    Вот что нагуглилось сходу:
    https://learn.microsoft.com/ru-ru/cpp/build/walkth...
    Ответ написан
    Комментировать
  • Как исправить ошибку в программе на С++, чтобы не вызывалось необработанное исключение или кнопка останова?

    @res2001
    Developer, ex-admin
    1. у вас в конструкторе выделяется динамическая память. Но деструктора у вас нет. Память не освобождается, после удаления объекта - утечка.
    2. Еще про конструктор. Внимательно посмотрите на строчку: coord = new double[size];.
    Чему равно значение переменной size в ней? Какого размера выделится буфер?
    3. Конструктор копирования реализован не правильно. Если вы создадите объект с его использованием, то он будет использовать тот же блок динамической памяти, что и исходный объект. Ни к чему хорошему это не приведет, вряд ли вы рассчитывали на такое поведение.

    На счет ошибки компилятора - хорошо бы увидеть текст ошибки. Условие в operator<< действительно всегда ложно. Надо сравнивать с vec.size - 1
    Ответ написан
    Комментировать
  • Почему tellg() неявно приводится к int при инициализации int, но не может быть сложенным с int?

    @res2001
    Developer, ex-admin
    попробуйте так: 2 + telg() На gcc вроде прокатывает без предупреждений.
    Но это опасно, т.к. tellg может вернуть ошибку (-1) и она в этом случае останется а) необработанной б) ваш код не поймет, что была ошибка - выражение вернет корректное положительное не правильное значение. Возможно именно по этому выдается предупреждение (в GCC предупреждение, а не ошибка).
    Ответ написан
    Комментировать
  • Как правильно написать функцию принимающую универсальную ссылку?

    @res2001
    Developer, ex-admin
    ifs может быть const

    Поток не может быть const. Когда вы читаете/пишете в поток у него меняются внутренние атрибуты. В const потоке вы ничего не сможете изменить, а следовательно ни писать туда ни читать из него не сможете - зачем он такой красивый нужен?
    как я понял нужно использовать и std::forward, но не понимаю куда его вписать корректно

    Если вы будете поток дальше передавать в какие-то функции, то заверните поток в forward. То же самое, если надумаете возвращать поток из функции.
    Ответ написан
  • Error LNK2019: ссылка на неразрешенный внешний символ public: void __cdecl Window::create(void)?

    @res2001
    Developer, ex-admin
    но window.cpp задумывается как часть библиотеки которую можно потом установить

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

    @res2001
    Developer, ex-admin
    Ситуация вполне вероятна. На сколько я знаю, нет гарантии, что поток после создания сразу получит процессор для выполнения.
    Делайте проверку в get в цикле, если x.load вернул false, то можно вызвать yield(), чтоб напрасно не жрать процессор в цикле.
    Ответ написан
  • Вылетает программа на C++ с кодом -1073741571 (0xC00000FD)?

    @res2001
    Developer, ex-admin
    Если вы под виндой, то запустите программу в дебагере без точек останова. Когда произойдет ошибка, то дебагер остановится в том месте где произошла ошибка, перейдете по стеку вызовов к вашему последнему вызову (т.к. ошибка может проявляться где-то в коде стандартной библиотеке) и начнете анализировать это место программы.

    В линуксах надо включить создание coredump в ОС, выполнить программу, получить файл coredump и проанализировать его в gdb. Схема анализа кода та же, что под виндой.

    Возможно у вас выход за пределы массива. При анализе внимательно смотрите на используемые индексы и размеры выделенного вектора к которому идет обращение.

    Результаты вашего анализа можете скинуть сюда.
    Ответ написан
    Комментировать
  • Что делать с ошибкой unresolved external?

    @res2001
    Developer, ex-admin
    У вас не включен в сборку Firm.cpp. Поэтому все что в нем определено и используется в unit1.cpp будет вызывать ошибку.
    Как собираете проект?
    Ответ написан
    Комментировать
  • С++ На всех ли платформах одинаковая точность операций с float?

    @res2001
    Developer, ex-admin
    Форматы чисел с плавающей точкой стандартизованы IEEE754.
    Скорее всего, операции выполняемые процессором над числами с плавающей точкой будут давать одинаковый результат на разных платформах.
    Но вот программные реализации различных математических алгоритмов из стандартной библиотеки, типа того же sqrt и т.п., могут отличаться.
    Для гарантии, вам нужно использовать для этих целей какую-то единую библиотеку для всех платформ, а не стандартный math.h.
    Ответ написан
    1 комментарий
  • Как быстро округлить timestamp до секунд, минут, часов, дней и т.д.?

    @res2001
    Developer, ex-admin
    Думаю, для округления вплоть до дней можно просто отбрасывать остаток от деления на соответствующую константу. Константы вычислить заранее для 1 дня, 1 часа и 1 минуты.
    Округление до года и месяца - по предложенной jcmvbkbc схеме.
    Ответ написан
    Комментировать