Утечки памяти с kbhit для Linux

Код находится здесь. Когда я запускаю свою программу (я сохранил kbhit как заголовочный файл и хранил его в папке моей программы), я получаю неинициализированный доступ для чтения при первом использовании kbhit (я использую DrMemory для отладки памяти). Я включил sys/ioctl.h, так как моя программа не могла использовать FIONREAD без него. Проблема заключается в вызове tcsetattr(STDIN, TCSANOW, &term); Я не совсем понимаю, как это работает, поэтому любая помощь будет оценена по достоинству. Спасибо!

Редактировать: точное сообщение «UNINITIALIZED READ: чтение 12 байт. системный вызов ioctl.0x5402, параметр #2». Строка из вызова tcsetattr(). Эта ошибка возникает после сохранения kbhit в виде файла cpp и создания его шаблона в другом файле. Программа работает нормально, за исключением одной ошибки.


person Vivek Verghese    schedule 18.02.2019    source источник
comment
Учитывая, что tcsetattr будет вызываться ровно один раз за время жизни программы, становится сомнительным называть это утечкой памяти, поскольку любое выделение, которое она производит, будет освобождено при выходе из процесса. Единственное, что я могу придумать, это то, что вы намекаете на сохранение kbhit в виде заголовочного файла. Означает ли это, что вы, возможно, связываете несколько копий этого кода для каждого исходного файла (и вызываете его из разных исходных файлов). Просто чтобы исключить невероятное, убедитесь, что у вас нет кода в файлах заголовков.   -  person selbie    schedule 18.02.2019
comment
Я сохранил функцию в заголовке и включил ее в основной файл, так как это единственное место, где я ее использую.   -  person Vivek Verghese    schedule 19.02.2019
comment
См. редактирование выше.   -  person Vivek Verghese    schedule 19.02.2019
comment
Вы вызываете tcgetattr для заполнения значений структуры перед вызовом tcsetattr. Верно?   -  person Zan Lynx    schedule 19.02.2019
comment
Эй, это также может быть ошибка, когда DrMemory не знает, что tcgetattr записывает в структуру. Я знаю, что у valgrind есть всевозможные пользовательские правила для определения подобных вещей для известных библиотек.   -  person Zan Lynx    schedule 21.02.2019
comment
@ZanLynx эй, я думаю, что использование valgrind сработало. Возможно, как вы сказали, проблема с самим DrMemory. Спасибо вам за помощь!   -  person Vivek Verghese    schedule 27.02.2019


Ответы (1)


Вот версия кода, который я модифицировал, чтобы он был настоящим C, а не C++, поскольку он был C++ только из-за небрежности с ключевыми словами bool true/false и struct.

И да, не помещайте это в заголовочный файл. Поместите его в файл с именем kbhit.c и удалите или закомментируйте тестовую основную функцию. И в заголовочном файле просто напишите строку:

int _kbhit(void);

Или вам может понадобиться:

extern "C" int _kbhit(void);

Это все, что вам нужно в шапке.

/**
 Linux (POSIX) implementation of _kbhit().
 Morgan McGuire, [email protected]
 */
#include <stdbool.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

int _kbhit(void) {
        static bool initialized = false;

        if (! initialized) {
                // Use termios to turn off line buffering
                struct termios term;
                tcgetattr(STDIN_FILENO, &term);
                term.c_lflag &= ~ICANON;
                tcsetattr(STDIN_FILENO, TCSANOW, &term);
                setbuf(stdin, NULL);
                initialized = true;
        }

        int bytesWaiting;
        ioctl(STDIN_FILENO, FIONREAD, &bytesWaiting);
        return bytesWaiting;
}

//////////////////////////////////////////////
//      Simple demo of _kbhit()

int main() {
        printf("Press any key");
        while (! _kbhit()) {
                printf(".");
                fflush(stdout);
                usleep(1000);
        }
        printf("\nDone.\n");

        return 0;
}

Мне это кажется правильным, и valgrind не жалуется. У меня нет доктора Памяти, чтобы проверить.

Как работает этот код, так это то, что он сначала использует tcgetattr для чтения структуры termios (настройки ввода-вывода терминала, я думаю). Затем он изменяет его, сбрасывая биты ICANON. Canon — это каноническая настройка для терминалов, включающая буферизацию строк. Затем он записывает новые значения termios обратно в терминал. с tcsetattr.

Вызов ioctl получает, сколько байтов ожидает в буфере. Если есть ожидающие байты, значит кто-то нажал какие-то клавиши.

person Zan Lynx    schedule 18.02.2019
comment
Пожалуйста, смотрите редактирование выше для получения дополнительной информации. Я изменил его с заголовка на файл cpp (чтобы я мог скомпилировать с *.cpp). Если нужно, могу выложить весь проект. Это просто Snake на C++. Мне нужен был способ читать ввод с клавиатуры, не прерывая игру, поэтому было необходимо создать функцию kbhit, которая работает с Linux. - person Vivek Verghese; 19.02.2019