正确使用GNU / Linux read()函数的方法

时间:2014-11-22 07:06:49

标签: c++ c linux pointers

在GNU / Linux的手册页中,读取函数的描述如下:

ssize_t read(int fd, void *buf, size_t count);

我想使用此函数从套接字或串行端口读取数据。如果count大于1,则函数参数中提供的指针将指向从内存中的端口读取的最后一个字节,因此指针递减是将指针带到数据的第一个字节所必需的。这很危险,因为在C ++这样的语言中使用它,根据容量的大小和空间需要对容器进行动态内存分配,可能会在read()函数返回时损坏数据。我想过使用C风格的数组而不是指针。这是正确的方法吗?如果没有,这样做的正确方法是什么?我使用的编程语言是C ++。

修改

导致描述情况的代码如下:

QSerialPort类用于配置和打开具有以下参数的端口:

  • 波特率115200
  • 8个数据位
  • 没有平价
  • 一站式
  • 无流量控制

并且对于读取部分,只要涉及stackoverflow,读取就完全按照以下方式执行:

包含以这种方式定义的std::vectorstruct的{​​{1}}:

struct DataMember 
{
    QString name;
    size_t  count;
    char *buff;
}

然后在while循环中,直到到达所提到的std::vector的末尾,基于所述read()的{​​{1}}成员变量和数据执行count存储在同一个struct的buff:

struct

然后在控制台上打印数据。在我的测试用例中,只要数据是一个字节,打印的值是正确的,但是对于多于一个字节,显示的值是从端口读取的最后一个值加上一些垃圾值。我不知道为什么会这样。请注意,ssize_t nbytes = read(port->handle(), v.at(i).buff, v.at(i).count); 更改为char *buff时会获得正确的结果。

3 个答案:

答案 0 :(得分:3)

  

如果计数大于1,则函数参数中提供的指针将指向从内存中的端口读取的最后一个字节

没有。指针按值传递给read()方法,因此,无论计数如何,在调用之后值完全和完全不可能完全不同,无论计数如何。

  

因此指针递减对于将指针指向数据的第一个字节是必要的。

指针已指向数据的第一个字节。不需要减量。

  

这很危险,因为在C ++这样的语言中使用它,根据容量的大小和空间需要动态分配容器可能会在read()函数返回时损坏数据。

这完全是胡说八道。

你错了这一切。

答案 1 :(得分:2)

  

在我的测试用例中,只要数据是一个字节,打印的值就是正确的但是对于多个字节,显示的值是从端口读取的最后一个值加上一些垃圾值。

从read(2)联机帮助页:

  

成功时,返回读取的字节数(零表示文件结束),   并且文件位置按此编号前进。如果这个数字是,那不是错误   小于请求的字节数;这可能发生在例如因为更少   字节实际上是可用的(也许是因为我们接近文件结尾,或者   因为我们正在从管道或终端读取数据,或者因为read()被中断了   通过信号。出错时,返回-1,并正确设置errno。在这种情况下   未指定文件位置(如果有)是否发生变化。

对于管道,套接字和字符设备(包括串行端口)和阻塞文件描述符(默认),读取实际上不会等待完整计数。在您的情况下,read()会阻塞,直到串行端口上有一个字节并返回。这就是为什么在输出中第一个字节是正确的,其余的是垃圾(未初始化的内存)。你必须在read()周围添加一个循环,如果你需要完全计数,那么它将被重复读取,直到读取了计数字节。

答案 2 :(得分:0)

  

我不知道为什么会这样。

但我知道。 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; } 需要一些额外的工作来正确处理信号和错误情况。

new

查看乱码的第一段:

  

如果计数大于1,则函数参数中提供的指针将指向从内存中的端口读取的最后一个字节

是什么让你这么认为?这不是它的工作原理。很可能你传递了一些未正确初始化的无效指针。任何事情都可能发生。

  

因此指针递减对于将指针指向数据的第一个字节是必要的。

不。 这不是它的工作方式。

  

这很危险,因为在C ++这样的语言中使用它,根据容量的大小和空间需要动态分配容器可能会在read()函数返回时损坏数据。

都能跟得上这不是它的工作方式!

C和C ++是一种显式语言。如果没有(程序员)明确地请求它,一切都在明显的情况下发生。没有请求发生这种情况时,不会分配内存。它可以是显式{{1}},某些RAII,自动存储或使用容器。但是在C和C ++中没有任何“突然出现”。在C和C ++中没有内置的垃圾收集^ 1。对象不会在内存中移动或调整大小,而无需在程序中明确编写某些内容以实现此目的。


[1]:您可以使用GC库,但那些 never 会踩到正在执行的代码可以访问的任何内容。基本上C和C ++的垃圾收集器库是内存泄漏检测器,它将释放正常程序流不再能够访问的内存。