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

    @res2001
    Developer, ex-admin
    for(int i = 1; i < 11; i+=2)
    Ответ написан
    Комментировать
  • Почему при умножении 1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17 получается совсем что-то не то?

    @res2001
    Developer, ex-admin
    Ответ в комментариях
    Ответ написан
    Комментировать
  • Почему не считывает русскую букву в char?

    @res2001
    Developer, ex-admin
    Если программа выполняется под виндой, то:
    1. В консоли винды по умолчанию кодировка cp866 (да же не cp1251 и тем более не unicode). Кодировка консоли в винде может быть легко изменена с помощью команды консоли chcp, так что полагаться на то, что в консоли будет всегда одна и та же кодировка не стоит. Когда вы читаете символ с консоли, то он появляется в программе в кодировке консоли.
    2. Еще у вас есть кодировка файла исходного кода. Компилятор сам ничего не перекодирует. В какой кодировке написаны ваши константы в исходниках в той они и будут в программе. Когда вы выполняете операцию сравнения, то символ в кодировке файла исходного кода сравнивается с символом в кодировке консоли. Если обе кодировки не совпадают - сравнение будет не удачным, даже если символ один и тот же.
    3. Простой вариант - писать исходники в cp866 и не менять кодировку консоли в тестах. Может не работать, если, например, запускаете программу из MSVC - IDE может сама менять кодировку своей отладочной консоли. Лучше тесты запускать вручную в отдельном консольном окне.
    4. Правильный вариант - исходники писать в UTF8, символьные и строковые константы писать как wide char (wchar_t, в винде это UTF-16) перед вводом узнать кодировку консоли, перекодировать введенные символы в UTF-16, сравнивать символы в UTF-16. С выводом в консоль - та же история. В WinAPI есть функции перекодировки из многобайтных (cp866/1251, ...) в широкие символы и обратно.

    В линуксе сейчас повсеместно консоль в unicode, соответственно и читать вам надо unicode и сравнивать с unicode.
    Ответ написан
    Комментировать
  • Как вставить новый массив в имеющийся?

    @res2001
    Developer, ex-admin
    В общем случае нужно:
    1. расширить принимающий массив на количество элементов во вставляемом массиве (обычно это перевыделение памяти с копированием содержимого старого массива в новый большего размера),
    2. переместить в конец часть старого массива после позиции вставки,
    3. вставить (скопировать/перенести) элементы вставляемого массива.

    Вставка новых элементов - это увеличение размера массива. Изменить размер можно только у динамического массива. Для автоматического и статического это сделать нельзя.

    В std::vector есть метод insert, который сделает всю работу за вас. Правда тут придется вставлять элементы по одному. В С++20, похоже, появилась перегрузка insert, где можно вставить несколько элементов сразу.
    Ответ написан
    1 комментарий
  • Как использовать библиотеку GLFW используя только g++?

    @res2001
    Developer, ex-admin
    1. добавьте в опции компилятору путь к файлам заголовков библиотеки (через опцию -I)
    2. путь к библиотечным файлам (опция -L)
    3. перечислите в опциях -l все библиотеки, которые будете использовать.

    Это стандартный путь для использования сторонних библиотек в вашей программе, не зависимо от конкретной библиотеки.
    Ответ написан
    2 комментария
  • Чем обусловлены различия в работе со строками и другими массивами?

    @res2001
    Developer, ex-admin
    void func()
    {
    char str1[] = "Hello";
    const char* str2 = "Hello";
    int* mas1 = { 4, 5, 7, 9 };  // compile error
    int mas2[] = { 4, 5, 7, 9 };
    char* str3 = {'s', 't', 'r', '3', '\0'};  // compile error
    char str4[] = {'s', 't', 'r', '4', '\0'};
    ...
    }

    Объявление str1 и str2 это не совсем одно и то же.
    str1 выделяет на стеке массив charов подходящего размера и копирует в этот массив строку "Hello". Строка "Hello" до копирования будет хранится в сегменте памяти для констант (rodata), который сформируется ОС автоматически при загрузку исполняемого файла в память. Таким образом строка str1 лежит полностью на стеке, ее можно изменять при желании, но увеличивать размер нельзя. str1 может быть как константной строкой, так и не константной.
    str2 - на стеке выделяется память под указатель, указателю присваивается адрес строки "Hello", которая все так же лежит в сегменте rodata. Поэтому const тут необходим, т.к. оригинальная строка, на которую ссылается str2 не изменяемая. Если убрать const у str2, то, например, gcc выдаст предупреждение по умолчанию, но код скомпилирует. Если дальше попытаться изменить str2 (например str2[0] = 'h';), то компилятор на присвоение уже ничего не скажет, т.к. str2 уже не константная строка, но при выполнении получишь segfault, т.к. пытаешься изменить read-only память.
    В случае инициализации mas1, выражение в фигурных скобках - это список инициализации. На основании списка инициализации компилятор генерирует код, который при выполнении проинициализирует массив mas1, т.е. он не генерирует массив, он генерирует код, что-то типа такого mas1[0] = 4; mas[1] = 5; ... при этом компилятор подразумевает, что память под mas1 уже должна быть выделена подходящего объема. Но в примере mas1 - это не массив - это указатель, который никуда не ссылается. Инициализировать указатель таким образом не возможно. Нужно делать так, как показано для mas2.
    Точно так же списком инициализации можно проинициализировать и массив для строки (str3) и получить ту же ошибку, что и для mas1.
    Из примеров можно сделать вывод, что для строк в языке существует специальный синтаксис, который больше нигде не применим - пример str2. Это так и есть.

    Сырые строки (Си строки, ASCIIZ строки, строки оканчивающиеся нулем) ни разу не устаревшие. Объект string, конечно удобный и прочее, но он медленный из-за того, что хранит свою строку в динамической памяти, и при изменении строки перевыделяет память по мере надобности и копирует строку в новую память. Операции выделения/удаления динамической памяти очень дорогие. Чаще всего string - вполне адекватный выбор, но там где нужна быстра работа со строками лучше поискать другие варианты, например сырые строки.
    В С++17 появился std::string_view - это обертка над константной сырой строкой, делает код немного более понятным, но не производит никаких манипуляций с динамической памятью - ему на вход надо дать сырую строку. Он ее и использует для своих операций. Из-за того, что string_view не изменяет свою строку и не манипулирует с памятью - он работает быстрее, но у него ограниченное количество операций - отсутствуют операции модифицирующие строку.
    Ответ написан
    3 комментария
  • Как разделить строку до и после определённого символа?

    @res2001
    Developer, ex-admin
    Вариант с "токенизацией" делит один единственный буфер на несколько Си строк, т.е. там просто записывается завершающий 0 вместо разделителей. Это быстро и довольно просто, ничего никуда не копируется, дополнительная память не выделяется. Но надо иметь ввиду, что используется один и тот же буфер и если вы его не аккуратно перезапишите (не важно по какому указателю на внутренние строки), то ваши строки "испортятся". Освобождая память под этим буфером надо, естественно, по указателю на начало буфера, а не по указателю на произвольную строку внутри буфера. Освободив буфер надо иметь ввиду, что все строки, содержащиеся в буфере то же освободились.

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

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

    Если же вам надо только "вывести подстроку", то можно просто искать позицию разделителя, с его помощью вычислять размер левой подстроки и используя printf("%*s", len, leftstr); выводить левую подстроку. Дальше сохраняем указатель на начало правой подстроки, ищем от него следующий разделитель, то, что раньше было правой подстрокой становится левой, точно так же выводим и т.д. в цикле.
    Вместо printf можно выводить так: cout << setw(len) << leftstr;
    Ответ написан
    Комментировать
  • Как изменить атрибуты в объекте C++ через ввод?

    @res2001
    Developer, ex-admin
    Ошибка, очевидно, тут: cin >> mark;
    У вас mark - это указатель (а не строка). При этом указатель никуда не указывает (не инициализирован). Следовательно память под чтение не выделена. Куда по вашему должна попадать прочитанная информация?
    Напомню, что указатель это целое беззнаковое число (размером в 4 или 8 байт в зависимости от платформы, могут быть и другие варианты, но это больше экзотика), которое интерпертируется как адрес в памяти. Если в указателе мусор (как сейчас), то запись по этому указателю вызовет SEGFAULT при выполнении программы, вам повезло - компилятор сам заметил эту ошибку, с SEGFAULTом разбираться обычно труднее.

    Но и это еще не все.
    В следующей строке вы создаете временный объект Car с помощью вызова конструктора, после выхода из Input этот объект сразу уничтожается.

    Вам нужно:
    1. выделить память под marka. Лучше всего использовать не char*, а std::string, тогда память будет выделяться автоматически.
    2. читать ввод не в mark, а в marka. То же самое и для других двух переменных класса.
    Ответ написан
    1 комментарий
  • Как ускорить работу скрипта?

    @res2001
    Developer, ex-admin
    Один из файлов (меньший) сразу читайте весь (блоками, об этом ниже) и каждую строку помещайте в std::unordered_set<std::string>.
    Второй файл - построчно проверяете находится ли строка в set.

    Читать файлы лучше не строками, а большими блоками (4Кб или больше). Наибольшее время занимают именно операции чтения файла. Читая файл большими блоками вы сокращаете количество операций чтения. Дальше работаете с блоком - вручную делите его на строки и т.д. Не забываете, что блок не обязан заканчиваться и начинаться ровно в конце или начале строки.

    Если еще более оптимизировать, то std::unordered_set<std::string> лучше заменить на std::unordered_set<const char*> или std::unordered_set<std::string_view>.
    Смысл в том, что std::string под каждую строку будет выделять динамическую память - выделение памяти само по себе медленная операции (конечно быстрее, чем чтение файла, но тем не менее), а тут вы сначала прочитаете кусок файла в большой блок который будет выделен в динамической памяти, а затем под каждую строку из этого блока будете еще раз выделять память. Поэтому лучше при чтении большими блоками первого файла выделять блок динамически и сразу прям внутри блока делить его на строки (т.е. добавлять символ '\0' в конце каждой строки) и указатель на начало каждой строки добавлять в set (или делать из него std::string_view и уже его добавлять в set).
    Естественно выделенные блоки первого файла вам придется учитывать вручную (например хранить указатели на блоки в векторе) и после освобождения setа освобождать каждый блок (а не каждую строку).
    Для второго файла будет достаточно одного блока.
    Ответ написан
    Комментировать
  • Как оценивается идентификатор функции без оператора вызова?

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

    @res2001
    Developer, ex-admin
    В стандарте С11 п.6.5.4 пишут, что лишние байты будут отброшены ("removes any extra range and precision").
    Какой результат будет, если типы знаковые и значение а отрицательное и не влазит в диапазон целевого типа не уточняется.
    В плюсовом стандарте я что-то ничего не нашел на эту тему, но по факту поведение аналогично Си.
    Ответ написан
    Комментировать
  • Задание создания динамического массива до первого отрицательного числа, как это сделать?

    @res2001
    Developer, ex-admin
    Лучше использовать std::vector. Просто вводите числа в промежуточную переменную. После ввода каждого числа проверяете отрицательно ли оно, если нет, то добавляете число в вектор. Если число отрицательное и вектор не нулевого размера, то сортируете вектор с помощью std::sort и выводите вектор на экран. Все.

    Вместо вектора можно использовать и "сырые" Си массивы в динамической памяти. Алгоритм тот же, но при добавлении каждого нового числа вам нужно выделить новый массив большего размера, скопировать содержимое старого в новый, старый удалить. Для перевыделения массива можно использовать realloc(), она выполняет все эти операции. Вектор скрывает эти операции от вас и делает все то же самое, но без вашего участия, так же в векторе есть оптимизация, позволяющая делать меньше перевыделений памяти.
    Ответ написан
    Комментировать
  • Как получить вывод команды с терминала в переменную?

    @res2001
    Developer, ex-admin
    Через файл - это самый простой вариант.
    Более технологичный вариант - запускать процесс с перенаправлением потоков stdin/stdout/stderr в каналы (pipe).
    В этом случае можно в режиме "on-line" получать вывод от запущенного приложения и генерировать вход для приложения. Этот сложнее, но дает гораздо больше гибкости в работе с запускаемым процессом.
    Ответ написан
    Комментировать
  • Можно мне книг по шаблонному программированию в С++?

    @res2001
    Developer, ex-admin
    Для теории есть книга "Шаблоны С++".
    Так же можно посмотреть исходники STL там шаблоны очень активно применяются. А так же и другие свободные библиотеки, например boost.
    Ответ написан
    1 комментарий
  • Есть ли смысл в уточнении типов данных чисел? И что это даст?

    @res2001
    Developer, ex-admin
    Смысл в этом есть. Будет меньше расходоваться память стека.
    Но только в том случае, если вы программируете для системы с ограниченными ресурсами (какой-нибудь эмбедед и т.п.). Там подобная экономия может быть оправдана, да и то не всегда. В обычных приложениях для винды или линукса подобная экономия "на спичках" никакого эффекта не даст.
    Ответ написан
  • Будет ли скомпилированный код C++ работать на всех архитектурах процессора?

    @res2001
    Developer, ex-admin
    Даже бинарник для х64 не будет работать под х32, что уж говорить о принципиально разных архитектурах.
    В исполняемом файле содержатся машинные команды для конкретной архитектуры процессора.
    Для другой архитектуры нужен другой исполняемый файл.
    То же самое актуально и для разных ОС (бинарники для линукс и винды не совместимы). Но, обычно, есть варианты запустить бинарник от другой ОС - виртуализация, wine в линукс, WSL в винде.

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

    @res2001
    Developer, ex-admin
    Одинаковые участки кода надо выделить в отдельные функции, передавать в функции нужные аргументы и возвращать результаты. Частично это у вас уже сделано с f21, но результат можно улучшить.
    Ответ написан
    Комментировать
  • Почему при умножении матриц, вводя разные значения при каждом новом запуске программы я получаю одни и те же размеры(не правильные)?

    @res2001
    Developer, ex-admin
    Хотелось бы больше конкретики, т.к. чтение у вас в двух функциях CreatMatr() и ReadMatrix().
    В CreatMatr() вы не освобождаете динамически выделенную память - это утечка. Используйте вместо "сырого" массива вектор.
    Так же не понятно, зачем вы вызываете CreatMatr() 3 раза?

    Предположу, что проблема в том, что в CreatMatr() вы не возвращаете вызывающему коду считанные значения n, m, l, поэтому в main эти значения никак не изменяются и равны 1.
    Ответ написан
    5 комментариев