POSIX读取(2),意外行为

时间:2013-05-18 09:15:27

标签: c posix undefined-behavior

我在学习测试中使用read(2)时遇到了一些问题。

代码如下:

#include <stdio.h>

int main() {
    size_t length;
    read(0, &length, sizeof(length));
    printf("input = %u\n", length);

    return 0;
}

我想这个代码将从stdio中读取8个字节(即ascii char),并将它们存储在length变量中。然后它将打印到stdout 8字节的相应unsigned int值。

所以,让我的测试如下:从linux终端运行这个程序,然后点击'enter'。我希望length的值只有10(换行符的ascii值)。

但是运行这个测试(很多次):

$ ./test
len = 4195338

但是这个版本的代码就像我期望的那样:

#include <stdio.h>

int main() {
    int a = 10;
    size_t length;
    int b = 123;
    ssize_t n = read(0, &length, sizeof(length));
    printf("input = %u\n", length);

    return 0;
}


$ ./test
input = 10

那么,有什么意义呢? 为什么我添加一些随机和未使用的变量,如果我存储read()的返回值,那么同一输入的输出会有所不同?

N.B。我知道read(2)是一个原始系统调用,不应该从终端读取输入,这只是一个学习问题。

2 个答案:

答案 0 :(得分:1)

如果size_t是8个字节,请在printf中尝试%llu

答案 1 :(得分:1)

如果sizeof(size_t) == 8,代码将读取8个字节 - 真。

通常,这些字节不会全部为ASCII(意味着某些字节将设置为第8位,值范围为0x80..0xFF,而不是ASCII的一部分)。

但是,没有字符转换。如果您的文件包含12345678,则值为0x3132333435363738(或者可能为0x3837363534333231)。如果您需要转换,请不要使用read(2)

printf()格式应该是%zu(C99)或%lu(C89 size_t相当于64位unsigned long;它不能是当然是unsigned long long和C89。

请注意,您的示例输出不是来自示例代码。示例输出显示为len = ...,但代码会生成input = ...。因此,您的一个问题可能是您没有测试您认为自己正在测试的内容。

你发表评论:

  

我知道read(2)是一个原始系统调用,不应该从终端读取输入。

read(2)系统调用(可能)由getchar()等函数用于从终端读取。使用它从终端读取是不正确的。从终端读取字符数组以外的东西可能是不正确的。


  

我只是从终端运行程序,然后从键盘输入。

喔。烦。我从未想过你会这样做。

好吧,你将一个字节的数据读入一个需要8个字节的变量,然后就会变成垃圾。您的变量未能可靠地初始化。

这是一个带样本输出的SSCCE(Short, Self-Contained, Correct Example):

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    size_t length = 0xFFFFFFFFFFFFFFFF;
    int nbytes = read(0, &length, sizeof(length));
    printf("nbytes = %d: input = %zu (0x%zX)\n", nbytes, length, length);
    return 0;
}

两个样本运行:

$ ./test

nbytes = 1: input = 18446744073709551370 (0xFFFFFFFFFFFFFF0A)
$ ./test
12345678
nbytes = 8: input = 4050765991979987505 (0x3837363534333231)
$ ./test < /dev/null
nbytes = 0: input = 18446744073709551615 (0xFFFFFFFFFFFFFFFF)
$

你看到那里发生了什么吗?请注意,SSCCE代码会关注并报告读取的字节数。始终检查类似读取操作的返回值非常重要(这里,具体来说,这意味着read());如果您没有获得预期的数据,您的结果可能不是您所期望的。使用“命中换行”之后的值可能是“未定义的行为”,尽管显示的行为是您通常得到的行为。

(在Mac OS X 10.8.3上使用GCC 4.7.1进行测试 - 英特尔芯片,小端。)