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

    @res2001
    Developer, ex-admin
    Массив может быть представлен по разному.
    Для статического массива sizeof(arr) сразу возвращает размер массива в байтах.
    Для указателя на массив sizeof(ptr_arr) возвращает размер указателя. Чтоб получить размер массива в этом случае надо сделать sizeof(ptr_arr[0])*N.
    Когда вы передает в функцию статический массив, то внутри функции это все равно указатель (не смотря на то, что в аргументе он может быть объявлен как int arr[N]) и sizeof над ним ведет себя как с указателем, а не как с массивом.

    Вариант sizeof(arr[0])*N будет правильно работать как с указателем так и со статическим массивом.
    При этом реального обращения к памяти arr[0] не происходит, поэтому этот вариант можно использовать даже в таком случае:
    int *ptr_arr=NULL;
    size_t size_arr = sizeof(ptr_arr[0])*N;
    Ответ написан
    Комментировать
  • В C++ есть TCP и UDP?

    @res2001
    Developer, ex-admin
    Базовый функционал сети предоставляет операционная система, там то же есть разделение на TCP и UDP (точнее на SOCK_STREAM и SOCK_DGRAM). Любое приложение, не зависимо от языка программирования, использует функционал, предоставляемый ОС, если каким-то образом не реализует его самостоятельно.
    В стандартной библиотеке С++ нет никаких оберток над базовыми функциями ОС. Но из коробки вы легко можете использовать базовые функции ОС, ничего отдельно ставить не нужно, все доступно в рамках системных библиотек и заголовочных файлов.
    Но для удобства можно использовать какую-нибудь библиотеку, обертку.
    Ответ написан
    Комментировать
  • Как сделать многопоток сокетов?

    @res2001
    Developer, ex-admin
    Почитать книжку "Многопоточное программирование на С++". Там не про сокеты в многопотоке, а просто про сам многопоток. Многопоточная обработка сокетов не сильно отличается от любого другого многопотока.

    Самый простой вариант - отдельный поток на каждого клиента.
    В одном потоке крутится accept, при получении нового клиентского дескриптора, запускаешь новый поток и передаешь ему дескриптор. Поток производит всю обработку запросов от клиента. При разрыве связи, поток завершается.
    Достоинтсва: просто в реализации, минимум межпоточной синхронизации.
    Недостатки: отдельный поток на дескриптор - это довольно дорого, но если не планируется обрабатывать соти клиентов одновременно, то вполне годный вариант.

    Как альтернатива для случая не слишком большого количества клиентов - один поток + неблокирующие сокеты + асинхронная обработка.
    Про работу сокетов в неблокирующем режиме можешь почитать: "UNIX: Разработка сетевых приложений" книжка старая, но актуальная. В магазине скорее всего не найдешь, но в интернете полно сканов.
    Книжка хороша не только по неблокирующим сокетам, а в принципе как описание разработки сетевых приложений.

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

    @res2001
    Developer, ex-admin
    Обычно, чтоб IDE нормально видела msys и mingw нужно сделать 2 вещи:
    1. Добавить переменные окружения:
    MSYS_HOME=c:\msys64\usr
    MINGW_HOME=c:\msys64\mingw64

    2. Добавить в переменную окружения PATH пути к каталогам bin msys и mingw. Я использую для этого такую строку (с учетом выполнения п.1): %MSYS_HOME%\bin;%MINGW_HOME%\bin

    После этого IDE без дополнительных настроек должна нормально найти установленный компилятор (mingw64), а компилятор сможет без дополнительных указаний видеть свои стандартные пути для библиотек и заголовков.

    CLion я не использовал, но подобная схема прекрасно работала с qtcreator и Eclipse .

    UPD: Сейчас посмотрел доку по msys2. Не нашел там упоминания про переменные MSYS_HOME и MINGW_HOME. Но с PATH все в силе. Так же рекомендуют установить переменную окружения MSYSTEM=MINGW64, которая задает окружение по умолчанию. Но в вашем случае это не обязательно, т.к. значение MINGW64 является значением по умолчанию. У этой переменной есть еще несколько вариантов.
    Ответ написан
    Комментировать
  • Как ограничить кол-во символов после точки при вводе данных в С++?

    @res2001
    Developer, ex-admin
    Никто ничего не дописывает. Дело в специфике хранения данных в числах с плавающей точкой.
    Эти числа всегда приближенные. Грубо говоря даже какое-то целое число, типа 3, будет храниться как: 2.9999999999999999999.
    Конкретно 3 будет иметь точное представление, но другие числа будут иметь приближенное представление. Поэтому работая с числами с плавающей точкой лучше всегда иметь ввиду, что это число приближенное.
    Можете почитать что-нибудь по стандарту IEEE754 - стандарт чисел с плавающей точкой, который сейчас используется повсеместно. Так же в интернете есть сайты, где можно глазами увидеть как представляется то или иное число с плавающей точкой в компьютере. Например можете тут посмотреть: https://www.h-schmidt.net/FloatConverter/IEEE754.html
    Ответ написан
    Комментировать
  • Не видит библиотеку, что делать?

    @res2001
    Developer, ex-admin
    в msys2 заголовки библиотеки ставятся в include/ncurses/
    Можете использовать так: #include <ncurses/ncurses.h>
    Можно использовать утилиту pkg-config для сборки, она выдаст правильные пути для заголовков и зависимости:
    pkg-config --cflags --libs ncursesw
    Ответ написан
  • Сmake не подключаеться библиотека curses,что делать?

    @res2001
    Developer, ex-admin
    Ошибка у вас не про библиотеку, а про
    add_executable(ctest ${SOURCES})
    Переменная SOURCES не определена там где она используется. Поэтому ctest получается без исходников - собирать нечего.

    Вообще обычно подключаю библиотеки через find_package или pkg_check_modules, но для этого надо использовать какую-то среду с пакетным менеджером и возможно поддержкой pkgconfig. Под виндой можно использовать msys2+mingw например - там все это есть. Или vcpkg для MSVS (или что там сейчас актуальное, не отслеживаю).
    Ваш вариант подключения то же должен работать, но надо еще указать путь к заголовочным файлам, чтоб было совсем красиво. Подобный способ не переносим, стоит поискать альтернативу.
    Ответ написан
    5 комментариев
  • Как настроить CMake в Clion для stm32 для подключения библиотек?

    @res2001
    Developer, ex-admin
    Попробуйте в include_directories() указывать полные пути. Стандартная переменная cmake ${CMAKE_CURRENT_SOURCE_DIR} содержит путь где лежит текущий файл cmakelists.txt, ее можно использовать для генерации полного пути.
    Обычно, если в проекте используется иерархия каталогов, то в каждом каталоге с исходниками лежит свой собственный cmakelists, а подключается он к нижележащему через add_subdirectory(). Собирайте в каждом каталоге свою цель (статическую библиотеку), потом эти цели указывайте в зависимостях для основной цели.
    Ответ написан
    Комментировать
  • Код работает по другому из-за защиты сервера?

    @res2001
    Developer, ex-admin
    Если очень хочется делать это в онлайн, то пользуйтесь другими онлайн-компиляторами.
    https://godbolt.org/ например. Sololearn выглядит в этом плане довольно скудно.
    Но тем не менее первый вариант выдал правильный результат.
    Можете собирать на своем компе ваши примеры.
    Ответ написан
  • Правильно ли я описал передачу параметров из функции С++?

    @res2001
    Developer, ex-admin
    Код надо вставлять текстом в соответствующем теге, есть кнопка в панельке.
    Передаешь правильно. Что есть сомнения? Просто запусти программу для проверки с разными значениями.
    Т.к. это С++ то можно использовать ссылки вместо указателей.
    Ответ написан
    Комментировать
  • Как правильно вернуть параметр и передать в main C++?

    @res2001
    Developer, ex-admin
    static void createCharacter(UserCharacter& character) {
    ...
    /* Возврат значений происходит через ссылку, переданную в параметре. 
    В return ничего возвращать не нужно. 
    Создавать другие объекты UserCharacter не нужно. */
    character.x = 10;
    character.y = 10;
    ...
    }
    
    int main() {
     UserCharacter character;
     ...
     createCharacter(character);
      ...
    }
    Ответ написан
    1 комментарий
  • Как происходит динамическое выделение памяти в С++?

    @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);
    Правда индексы придется вычислять ручками или можно оформить это в виде своего класса и переопределить операцию индексации.
    Выделять память одним большим куском для многомерных массивов - это правильно.
    Ответ написан
    Комментировать