на страницах руководства GNU/Linux функция чтения описывается следующим кратким описанием:
ssize_t read(int fd, void *buf, size_t count);
Я хотел бы использовать эту функцию для чтения данных из сокета или последовательного порта. Если count
больше единицы, указатель, предоставленный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти, поэтому декремент указателя необходим для приведения указателя к первому байту данных. Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read()
. Я думал об использовании массива C-стиля вместо указателя. Это правильный подход? Если нет, то каков правильный способ сделать это? Язык программирования, который я использую, - C++.
РЕДАКТИРОВАТЬ:
Код, который вызвал описанную ситуацию, выглядит следующим образом:
Класс QSerialPort
использовался для настройки и открытия порта со следующими параметрами:
и для части чтения, если речь идет о потоке stackoverflow, чтение выполняется точно так же, как это:
std::vector
содержащий ряд struct
определенных следующим образом:
struct DataMember
{
QString name;
size_t count;
char *buff;
}
то в течение цикла while до достижения конца указанного std::vector
, read()
выполняется на основе переменной члена count
указанной struct
и данные сохраняются в том же struct
buff:
ssize_t nbytes = read(port->handle(), v.at(i).buff, v.at(i).count);
а затем данные печатаются на консоли. В моем тестовом примере, пока данные являются одним байтом, значение напечатано правильно, но для более чем одного байта отображаемое значение является последним значением, которое было прочитано из порта, а также некоторыми значениями мусора. Я не знаю, почему это происходит. Обратите внимание, что правильный результат получается, когда char *buff
изменяется на char buff[count]
.
Я не знаю, почему это происходит.
Но я знаю. char *
- это просто указатель, но этот указатель должен быть инициализирован чем-то, прежде чем вы сможете его использовать. Без этого вы вызываете неопределенное поведение, и все может случиться.
Вместо значения size_t count;
и char *buff
вам нужно просто использовать std::vector<char>
, прежде чем делать вызов чтения, изменить его размер до количества байтов, которые вы хотите прочитать, затем взять адрес первого элемента этого вектора и передать это читать:
struct fnord {
std::string name;
std::vector data;
};
и использовать его так: что использование read
требует некоторой дополнительной работы для правильной обработки сигналов и условий ошибки.
size_t readsomething(int fd, size_t count, fnord &f)
{
// reserve memory
f.data.reserve(count);
int rbytes = 0;
int rv;
do {
rv = read(fd, &f.data[rbytes], count - rbytes);
if( !rv ) {
// End of File / Stream
break;
}
if( 0 > rv ) {
if( EINTR == errno ) {
// signal interrupted read... restart
continue;
}
if( EAGAIN == errno
|| EWOULDBLOCK == errno ) {
// file / socket is in nonblocking mode and
// no more data is available.
break;
}
// some critical error happened. Deal with it!
break;
}
rbytes += rv;
} while(rbytes < count);
return rbyteS;
}
Если счетчик больше единицы, указатель, указанный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти
Что заставляет вас думать так? Это не так, как это работает. Скорее всего, вы передали какой-то неверный указатель, который не был правильно инициализирован. Все может случиться.
поэтому декремент указателя необходим для приведения указателя к первому байту данных.
Неа. Это не так, как это работает.
Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read().
Неа. Это не так, как это работает!
C и C++ являются явными языками. Все происходит просто, и ничего не происходит без вас (программист), явно запрашивающего его. Не выделяется память без запроса об этом. Это может быть либо явный new
, некоторый RAII, автоматическое хранение или использование контейнера. Но ничего не происходит "из ничего" в C и C++. Там нет встроенной сборки мусора ^ 1 в C или C++. Объекты не перемещаются в памяти или не изменяют размер, если вы явно не кодируете что-то в свою программу, чтобы это произошло.
[1]: Существуют библиотеки GC, которые вы можете использовать, но они никогда не будут топать на все, что может быть достигнуто с помощью выполняемого кода. По сути библиотеки сборщиков мусора для C и C++ являются детекторами утечки памяти, которые освобождают память, которая больше не может быть достигнута при нормальном потоке программы.
Если счетчик больше единицы, указатель, указанный в аргументе функции, указывает на последний байт, который был прочитан из порта в памяти
Нет. Указатель передается методу read()
по значению, поэтому совершенно невозможно, чтобы значение было иным после вызова, чем было раньше, независимо от количества.
поэтому декремент указателя необходим для приведения указателя к первому байту данных.
Указатель уже указывает на первый байт данных. Никакой декремент не требуется.
Это опасно, потому что использование его на языке, таком как C++, с его динамическим распределением памяти в контейнерах в зависимости от их размера и пространственных потребностей может привести к повреждению данных в точке возврата из функции read().
Это все ерунда, основанная на невозможности.
Вы ошибаетесь во всем этом.
В моем тестовом примере, пока данные являются одним байтом, значение напечатано правильно, но для более чем одного байта отображаемое значение является последним значением, которое было прочитано из порта, а также некоторыми значениями мусора.
Из прочитанной (2) man-страницы:
При успехе возвращается количество прочитанных байтов (нуль указывает конец файла), а позиция файла продвигается по этому номеру. Это не ошибка, если это число меньше количества запрошенных байтов; это может произойти, например, из-за того, что на данный момент доступно меньше байтов (возможно, потому, что мы были близки к концу файла или потому, что мы читаем из канала или с терминала), или потому, что read() был прерван сигнал. При ошибке возвращается -1, а errno устанавливается соответствующим образом. В этом случае не указывается, изменяется ли позиция файла (если таковая имеется).
В случае труб, сокетов и символьных устройств (включая последовательные порты) и чтения файлового дескриптора блокировки (по умолчанию) на практике не дожидается полного счета. В вашем случае read() блокирует, пока байт не войдет в последовательный порт и не вернется. Вот почему на выходе первый байт правильный, а остальное - мусор (неинициализированная память). Вы должны добавить цикл вокруг read(), который повторяется до тех пор, пока количество байтов не будет считано, если вам нужен полный счетчик.
count
сообщает функции, насколько велик массив, на который указываетbuf
, чтобы он не перезаписывал конец массива.