Задать вопрос
Ответы пользователя по тегу C++
  • Как запросить число через cin в C++ чтобы оно заканчивалось на цифру 5?

    @Mercury13
    Программист на «си с крестами» и не только
    Это невозможно, мэн волен ввести в консоль что угодно.
    Но ваша задача как программиста — каким-то образом не допустить некорректного ввода. То ли вывести ошибку, то ли самостийно округлить, то ли потребовать повторный ввод… Вы уж сами думайте, что требуется именно от вашей программы.
    Условие «кончается на 5» эквивалентно  «не делится на 10, но делится на 5».
    Ответ написан
  • Как работает данная программа C++?

    @Mercury13
    Программист на «си с крестами» и не только
    Это обычная учебная программа и я за такой код бил бы по рукам. Нарекания к ней.
    1. Одновременное пользование printf и cout. Впрочем, несмотря на глючность, printf — хорошая штука, я и сам сделал более мощный аналог.
    2. Есть немного случаев, когда допустимы такие названия переменных/функций.
    • для счётчика цикла (i, j, k для переменной, it, jt, kt для итератора, u, v, w для новой фичи Си++11 — того, на что итератор указывает);
    • если мы преобразуем научную статью в код, и переменные так названы в статье.
    3. Даже название Count слишком расплывчатое — лучше что-то вроде nFound.
    UPD2. 4. Я бы переписал функцию P так, что for [0…горизонталь), при бое return false, цикл удался — return true.
    5. В музей говнокода!
    if (i == k)
        return true;
      else if (i != k)
        return false;
      else
        return !true && !false;

    Верно
    return (i==k);

    Программа проходится по 1-й горизонтали и ставит ферзя на каждую клетку. Если поместился — рекурсивно делает то же самое для 2-й горизонтали, если нет — значит, не повезло. Как только расчёт дойдёт до N-й горизонтали — мы нашли расположение ферзей, можно выводить.

    UPD. Ну, допустим, я бы переписал 2-ю функцию так (не меняя порядок параметров).
    void Backtracking(int currX, int &nFound, int boardSize, int queenY[])
    Ответ написан
    5 комментариев
  • Как правильно использовать строки в плюсах?

    @Mercury13
    Программист на «си с крестами» и не только
    • std::string — как правило, если не указано противное.

    • QString, AnsiString/UnicodeString и прочие — в соответствующих фреймворках, обычно очень близко к интерфейсным функциям.

    • char* — практически не используется в реальном коде. В основном для оптимизации, если есть собственное управление памятью. Довелось как-то в собственном разборщике XML (работает в 2,5 раза медленнее рекордсмена, pugixml. Зато даже это в разы быстрее Excel’я, пространства имён «из коробки», расход памяти мизерный и программирование простейшее.)
    Зато по-чёрному используется его const-аналог.

    • const char*. Это может быть одинокий const char* + нуль-терминированная строка, или указатель+длина, или указатель на начало + указатель за конец.
    1. Если ожидается, что в функцию будем передавать строковый литерал.
    void writeEnum(st::Stream& st, int value, const char* names[]) {}
    
    enum class Letter { A, B, C, …, Z, NN };
    const char* natoNames[static_cast<int>(Letter::NN)] = { "alpha", "bravo", "charlie", … };
    writeEnum(someStream, static_cast<int>(Letter::E), natoName);

    2. Если операцию со строкой можно произвести «на месте», не заводя новую память: (trim, как известно,— обрезка пробелов в начале и конце)
    void trim(const char*& beg, const char*& end);

    3. Если структура данных паразитирует на чужих строках, не заводя своей памяти. Особенно если конструкция строк неизвестна (например, при передаче данных из плагина в плагин).
    struct ParasiteString { const char *beg, *end; };

    4. В библиотеках, если они реально настолько компактные, что нет нужды обязательно подключать жирный STL.

    • char[] — только как оптимизация, когда предельная длина строки известна и невелика.
    wchar_t* myFtos(double value, wchar_t* buf, const FloatFormat& format) {}
    
    wchar_t buf[100];
    myFtos(100.500, buf, FloatFormat::NICE);
    Ответ написан
    Комментировать
  • Как упаковать файл в проект и затем извлечь на C++ в Visual Studio?

    @Mercury13
    Программист на «си с крестами» и не только
    Есть три способа хранения картинки в EXE-файле.
    1. Bitmap-ресурсом. Несжатая, большинство библиотек позволяют загрузить её в одну строчку.
    2. Двоичным ресурсом. Сжатие возможно любое, загрузка пишется несколько дольше.
    3. Массивом, const unsigned char picture[] = { };

    Я не знаю, с какой библиотекой для BMP вы работаете, и надо смотреть уже по месту.

    И наконец: для чего её распаковывать? Реально нужно? (Например, мне однажды нужно было распаковывать картинки, потому что не хотел писать просмотрщик графики и пользовался штатным.)
    Если нужно — то куда-нибудь во временный каталог, который тоже нужно сначала получить.
    Ответ написан
    4 комментария
  • Как создать полностью пустое приложение на С++ в QT Creator?

    @Mercury13
    Программист на «си с крестами» и не только
    Простой способ (говорят, что есть глюки!)
    import QtQuick 2.9
    import QtQuick.Window 2.2
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
        flags: Qt.Window | Qt.FramelessWindowHint   // <<<<<<
    
        Text {
            id: text1
            x: 77
            y: 59
            text: qsTr("Test!!!")
            font.pixelSize: 12
        }
    }


    Сложный описал Александр Таратин, и в нём эти глюки решены.
    Ответ написан
    Комментировать
  • С++ В чем отличие #include "*" и #include?

    @Mercury13
    Программист на «си с крестами» и не только
    Первое — это include собственного заголовка, то есть искать его в рабочем каталоге, в дополнение к стандартным каталогам компилятора.

    #include <file>
    This variant is used for system header files. It searches for a file named file in a standard list of system directories. You can prepend directories to this list with the -I option (see Invocation).

    #include "file"
    This variant is used for header files of your own program. It searches for a file named file first in the directory containing the current file, then in the quote directories and then the same directories used for <file>. You can prepend directories to the list of quote directories with the -iquote option.
    Ответ написан
    Комментировать
  • Почему чтение не разрешено?

    @Mercury13
    Программист на «си с крестами» и не только
    Отлично, вы уже запустили какой-то код. Но соглашение вызова так и не выдержали.
    1. Используйте соглашение вызова STDCALL. Тогда функция должна будет сама подчищать за собой. Поскольку в ней нет никаких локальных переменных, и подчистка не потребуется.
    typedef uint32_t WINAPI (*SomeFunc)();
    uint32_t result = (SomeFunc)exec();

    Заодно это позволит увидеть, что функция запустилась. В любом случае возвращаемое значение будет в eax.
    2. В конце нашей функции поставьте RET (опкод CB, если я не ошибаюсь).
    3. Вычислите и впатчите в наш код адрес переменной.

    я думал тут адрес указывается относительно текущего сегмента, как тогда быть?

    Начиная с защищённого режима 386, у нас «плоская память». Сегменты используются только загрузчиками, ВМами и прочей шушерой. Просто вычислите 32-битный адрес и запишите куда надо.
    Ответ написан
    Комментировать
  • Из-за чего ошибка чтения?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы компилируете функцию в двоичный код и на месте же вызываете. Так что есть вопросы.
    1. Функции обеспечили правильное соглашение вызова?
    Мне что-то кажется, что при подобной ручной компиляции проще работать с соглашением PASCAL или STDCALL — ну, шут его знает.
    2. VirtualProtect работает с целыми страницами. Так что на куске памяти из сегмента данных (ну или из стека, полного кода не вижу) она в лучшем случае откажет, в худшем ничего не сделает. Проверьте, была ли ошибка.
    3. Первый параметр должен быть source, а не &source.
    Ответ написан
    Комментировать
  • Как исправить ошибку при чтении файла на с++?

    @Mercury13
    Программист на «си с крестами» и не только
    Велика вероятность, что ошибка в mods[mode]. Четырнадцать значений, записанных как 4+4+3+3 — что-то странное. Вы явно хотели сделать «кубик» из четырёх флагов, от младшего к старшему — W, A (причём работает как простое W, если A без W), B, RW
    Ответ написан
  • Что означает эта строка(модификатор в макросе)?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут есть два модификатора.

    1. extern "C" — говорит, что функцию НЕ надо «козявить» на манер Си++, кодируя в имени её параметры наподобие h(int) → _Z1hi. Используется для доступа из Си++ к функциям, которые скомпилированы другим компилятором (Паскалем, Си…), а также для всяких там функций, которым надо дать фиксированное имя — например, функций DLL.

    2. __declspec(dllimport). Это нужно для доступа к функциям DLL: линкер будет брать функции прямо из DLL, без создания *.lib или *.a (как минимум MinGW так работает). Языки, сильно привязанные к определённой ОС (например, Delphi) могут создавать свои ключевые слова для доступа, например, к DLL или COM.
    procedure DoSomething(x : integer); cdecl;  external 'something.dll';

    Застандартизированный Си вынуждает полагаться на ключевые слова, начинающиеся с двух подчерков.
    Ответ написан
    Комментировать
  • Зачем нужен указатель на void?

    @Mercury13
    Программист на «си с крестами» и не только
    void* используется как указатель на сырые байтовые данные, не имеющие конкретного типа.
    Обычно это используется…
    1. В чтении-записи в файлы и на устройства, когда мы можем писать туда абсолютно любые типы.
    2. В «многоликих» функциях, которые могут принимать данные разных типов (malloc/calloc, часть функций WinAPI и ODBC).
    3. Как дескриптор — указатель, который запрещается разыменовывать. В Си для этого также часто используют указатель на недоопределённый тип, в Паскале с другими правилами эквивалентности типов — на пустой record. Но только пока не появится очередная многоликая функция вроде CloseHandle.
    4. Для обеспечения т.н. замыкания — передачи callback’у контекста, откуда была вызвана функция, вызвавшая callback.
    BOOL WINAPI EnumWindows(
      _In_ WNDENUMPROC lpEnumFunc,
      _In_ LPARAM      lParam
    );
    
    BOOL CALLBACK EnumWindowsProc(
      _In_ HWND   hwnd,
      _In_ LPARAM lParam
    );

    Вот этот LPARAM, который обычно определяется как какой-то указатель, и есть замыкание. Функция EnumWindows обещает передать его в функцию lpEnumFunc без изменений.
    (В Си++ для этого также используют виртуальные интерфейсы, но такой метод, сами понимаете, языкозависим и не годится для межъязыкового API.)

    Что происходит на стороне функции? Одно из двух (считаем, функция написана на ЯВУ).
    1. Либо вызывается некая функция устройства, которая говорит: «записать 100 байт», и дальше уже работает железо.
    2. Либо мы преобразуем void* в нужный нам тип и работаем с ним.

    Типы указателям дают по трём причинам.
    1. Вы забыли про операцию «разыменовать указатель». Чтобы его разыменовать, он должен иметь тип!
    2. Чтобы не ошибаться и не переприсвоить несовместимые указатели.
    3. Для полиморфизма — в Си++, давая delete x, мы даже можем не хранить, сколько байтов в блоке, поскольку мы знаем длину типа. (Есть ещё и виртуальные классы, но это другой вопрос.)
    Ответ написан
    Комментировать
  • Что за WINAPI, CALLBACK перед названиями функций?

    @Mercury13
    Программист на «си с крестами» и не только
    На x86 оба они — макроопределения для нестандартного соглашения вызова __stdcall.
    На ARM они ничего не делают.

    Соглашение вызова — это…
    • как на уровне регистров мы вызываем функцию;
    • кто подчищает стек за вызывающим;
    • кто отвечает за восстановление регистров, если они менялись (или есть риск, что они менялись).

    stdcall — вызов через стек, справа налево, за очистку стека отвечает функция, результат в eax (rax), функция отвечает за восстановление сегментных регистров, esp и ebp, программа за остальные.

    На ARM используется соглашение cdecl. То же самое, но за очистку стека отвечает программа (что там на ARM с регистрами, я не в курсе).
    Ответ написан
    Комментировать
  • Зачем в абстрактном базовом классе создавать конструктор?

    @Mercury13
    Программист на «си с крестами» и не только
    Абстрактные классы делят на интерфейсы и частично реализованные. Грань между ними такова:
    • Интерфейс не имеет данных.
    • У интерфейса все неабстрактные виртуальные методы представляют собой или эталонное поведение, или самую частую реализацию. В обоих случаях, если что, их надо не расширять, а переписывать с нуля.

    Так вот, для интерфейсов таких конструкторов, разумеется, не нужно.

    Например, между абстрактным потоком и файлом Win32 может быть такая иерархия: Stream → HandleStream → File. Stream — интерфейс, даже если там есть что-то типа
    // virtual
    unsigned long long Stream::remainder() const { return size() - pos(); }


    HandleStream содержит уже данные (дескриптор Win32), и это уже частично реализованный класс, который крутится вокруг этого дескриптора: в деструкторе вызов CloseHandle, конструктор может принимать дескриптор, полученный каким-то «левым» образом.
    HandleStream::HandleStream(HANDLE aHandle) : fHandle(aHandle) {}
    HandleStream::~HandleStream() { close(); }
    
    void HandleStream::close()
    {
      if (Handle != INVALID_HANDLE)  { // не помню, как там эта константа в Win32
        CloseHandle(fHandle);
        fHandle = INVALID_HANDLE;
      }
    }

    Вот в таких полуреализованных классах, разумеется, конструктор может инициализировать те данные, которые там есть.
    Ответ написан
  • Как вернуть массив строк C++?

    @Mercury13
    Программист на «си с крестами» и не только
    ВНИМАНИЕ: У вас есть пара тонких мест.
    РАЗ. не забудьте, что строковые литералы в Си — это нуль-терминированные строки, и "\0" по факту будет пустой строкой. Строку из одного нулевого символа можно загнать в string — например, конструктором string(begin, end) или s += '\0', но не конструктором string(const char*).
    ДВА. Этот static будет инициализирован при первом вызове. Второй вызов вернёт тот же массив.
    ТРИ. Строчку лучше передавать как string *func(const string& s) {}

    К делу. Массив — это довольно сложный объект, и возникает вопрос: кто этим массивом будет владеть после того, как он покинет пределы функции?
    1. Владеет система времени выполнения. Это отлично ответил Роман, дам только ключевую строчку.
    string* arr = func(exp);
    НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ: если массив используется в чьих-то «конских» конструкторах-деструкторах статических объектов (из-за отсутствия модулей Си++ не позволяет установить на уровне языка порядок создания/уничтожения статических объектов).

    2. Владеет какой-то объект.
    string *func(string s) {
        static string* ar = nullptr;
        if (ar) delete[] ar;
        ar = new ar[3];
        ar[0] = "qwe";
        ar[1] = s;
        ar[2].clear();
        return ar;
    }

    НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ: ну, это уж разбирайтесь сами с этим объектом, сколько он будет жить и насколько долго он будет держать массив. В данном случае каждый новый вызов уничтожает старый массив. (Код корявый, потому что последний массив не уничтожается.)

    3. Владеет тот, кто вызывает. Лучше для таких целей использовать какой-нибудь std::vector.
    vector<string> func(string s) {
        vector<string> r;
        r.reserve(3);
        r.push_back("qwe");
        r.push_back(s);
        r.push_back(std::string());
        return r;
    }
    Ответ написан
    Комментировать
  • Объясните пожалуйста смысл строк(указатели)?

    @Mercury13
    Программист на «си с крестами» и не только
    Перед нами структура данных под названием «односвязный список». У каждого элемента ссылка на следующий, у всей очереди ссылка на голову (front) и иногда на хвост (rear).

    Для чего нужна проверка на заполненность — непонятно, ведь ёмкость списка не ограничена и единственный способ убедиться, что очередь полна — завести память под новый элемент. Выпадает авария std::bad_alloc — значит, памяти не хватило. Если только для каких-то прикладных нужд: так, в StarCraft очередь на строительство пять юнитов, и точка. (Есть другой тип очереди, т.н. циклическая очередь — вон там ограничено.)

    Как работает enqueue: создаём новый элемент, следующий за ним — nullptr. Если очередь пуста, направляем на него front и rear. В противном случае пристраиваем его за тем, на который «смотрит» rear, и перенаправляем rear. Таким образом, «направляем rear» можно вынести за скобки, а остальные два исполнить в зависимости от пустоты очереди.
    Ответ написан
    3 комментария
  • Расскажите в каких ситуациях используют класс quiloader в QT?

    @Mercury13
    Программист на «си с крестами» и не только
    В крайне-крайне редких случаях, когда вы пишете редактируемый пользовательский интерфейс. Например, в ERP.
    Разумеется, QUiLiader под капотом есть всегда, но среднему программисту он ни к чему.
    Ответ написан
    Комментировать
  • Где ошибка в самодельном strcmp?

    @Mercury13
    Программист на «си с крестами» и не только
    Ваш код коряв (и пишет за пределы буфера, если в потоке действительно окажется 2048 байт) но нуль-терминированные строки отрабатывает правильно.

    TCP работает сплошным потоком, и если вы послали в сокет нечто ещё, кроме «123123», он считает весь поток до 2048 байт и, разумеется, строки не совпадут: с одной строны «123123», с другой, например, «123123qwe».

    Вам надо своими силами разбивать TCP-поток на сообщения — например, тем самым нулевым символом, CR или двумя/четырьмя байтами длины пакета.
    Ответ написан
  • Какова очередность присваивания нового объекта?

    @Mercury13
    Программист на «си с крестами» и не только
    А это значит, что манипулируя членами b я буду редактировать память a?

    Нет, конечно.

    Type& Type::operator=(const Type& x);
    Задача операции присваивания — сделать, чтобы объект слева (он же *this) стал копией объекта справа (он же x).

    Почему операция присваивания берёт Type& по ссылке? Да потому, что взять по копии — это вызвать конструктор копирования. Вполне можно написать
    BigInt& BigInt::operator = (int x);
    когда слева «большой» тип, а справа — «маленький», и его конструктор копирования незначителен (а то и равняется накладным расходам на ссылку, как у того же int). Но когда с обеих сторон один и тот же тип — это часто бездумный вызов конструктора копирования (собственные конструктор копирования и операцию = обычно пишут, когда нужна хитрая логика, а не тривиальное копирование).

    Почему операция присваивания возвращает Type&? Просто чтобы работали конструкции типа a = b = c. Может и void возвращать, если хочешь.

    class C
    {
    public:
        C() {}
        C (const C&) { std::cout << "Copy ctor" << std::endl; }
        C & operator = (const C&) { std::cout << "Assign" << std::endl; return *this; }
    };
    
    int main()
    {
        C a;
        C b = a;

    Выведет «Copy ctor». То есть ответ на первый вопрос — конструктор копирования.

    В литературе что я читаю это называется присваиванием

    Не присваивание, а инициализация. Присваиванием было бы…
    Type b;
    b = a;


    Type b = Type(a);

    В идеале тут два конструктора копирования и один деструктор, однако компилятору даётся воля уменьшать их количество. Потому только «copy ctor».

    Код
    int a = 10;
    C b = C(a);

    также даёт один вызов C(int). А C(const C&) не вызывается ни разу.

    UPD. Одно из нововведений Си++11 — чтобы даже без этих негарантированных оптимизаций, когда один объект исчезает, а второй появляется, можно было вместо копирования проводить нечто более простое, не требующее отвода-возврата памяти. Я-то экспериментировал в режиме Си++03, но в Си++11 уже были бы конструктор копирования, конструктор перемещения и деструктор.
    Ответ написан
    Комментировать
  • В чем здесь ошибка (перегрузка операторов)?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы наладили операцию String << ostream → ostream. Надо наоборот, ostream << string → ostream.

    Такую операцию можно наладить только за пределами класса (возможно, как friend).
    Ответ написан
    1 комментарий