使用unistd.h中的read() 函数只读取一个字节
我正在尝试启动一个Hangman项目,首先我想处理如何从终端获取用户字母。我知道我可以只读取一个字节并把该值存储到一个指针的地址里
char *ptr;
int read_bytes;
read_bytes = read(0, ptr, 1);
//NULL checks
但现在我写了一个函数,它返回一个以NULL结尾的缓冲区,只有两个字符(用户的输入和结尾的NULL字符)。
这是代码。
#include "utils.h"
char *read_one_byte(int fd)
{
char *buf;
int read_bytes;
if (!(buf = malloc(sizeof(char) * 2)))
return (NULL);
if ((read_bytes = read(fd, &buf, 1)))
{
buf[1] = '\0';
return (buf);
}
else
return (NULL);
}
将缓冲区NULL结束,即使它的内容只有一个字符,值得吗,还是我根本就不应该在意?
解决方案
我知道我可以只读取一个字节并把该值存储到一个指针的地址里 [...]
不,你不能。
要使用 read(),你需要的第一件事是在某个对象中存放结果,通常称为“缓冲区”。read() 的第二个参数是缓冲区的地址——一个指针类型的值。read不能也不会把任何东西存入该地址。成功时,它会把数据存储在指向的地址处的对象中,或者如果按照C 的语义规则没有定义这样的对象,那么C 不定义该行为。若你走运,在这种情况下你的程序会崩溃。
在你的示例中,你没有缓冲区。对 read() 调用的第二个参数是一个未初始化指针变量的未确定值。就实际情况而言,这个值可能指向任意对象,或者指向空,但在许多常见的机器上,“none” 要比其他情况更可能。C的语义对这个问题并不会给出答案。
你使用一个指针变量是为了存放另一个对象的地址。你并不需要这样的变量,常常也不想要它,当你只需要“使用”一个对象的地址时。这就是取地址运算符(&)的用途,在一类常见的特殊情况下,数组到指针的衰减也能在没有任何运算符的情况下完成这项工作。
char c;
int read_bytes;
read_bytes = read(0, &c, 1);
// check read_bytes
另外要注意,只有在相当特殊的情况下,才会有空指针检查的相关性。你确实需要检查返回值,但这并不是一次空指针检查。
但现在我写了一个函数,它返回一个以NULL结尾的缓冲区,只有两个字符(用户的输入和结尾的NULL字符)。
这是可行的,但你做错了。如果你动态分配缓冲区的内存——你当然可以——那么从 malloc() 返回的指针就是要传给 read() 的值。你却传递了存放指针的变量的地址,这显然不同也是双重错误:读取的值不会放到你想要的位置,并且在成功读取时,你还会破坏存储的指针值。你应该这样写:
// ...
if (!(buf = malloc(2))) return NULL; // sizeof(char) == 1 by definition
if ((read_bytes = read(fd, buf, 1))) // note "buf" as opposed to "&buf"
// ...
即使缓冲区的内容只有一个字符,是否值得将其NULL结束?还是根本不必?
这取决于你打算如何使用它。如果把它作为C 字符串,那么该字符串必须以一个值为0 的字节结尾。否则,终止它可能就没有什么好处。