【发布时间】:2015-09-21 12:46:30
【问题描述】:
我有一个函数可以使用read() 系统调用读取文件,并返回带有从文件读取的数据的char 指针。如有必要,该函数会重新分配空间。在特定点之后,读取失败并出现错误“错误地址”。失败的最小代码如下所示:
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
const unsigned BUFSIZE = 8192;
typedef struct
{
char* buffer;
long size;
} string_t;
string_t read_file(const char* path)
{
string_t error = { .buffer = NULL, .size = -1 };
int fd = open(path, O_RDONLY);
if (fd == -1) {
perror("open() failed in read_file");
return error;
}
string_t s;
s.buffer = malloc(BUFSIZE * sizeof(char));
s.size = 0;
int nread = 0;
long total_read = 0;
while ((nread = read(fd, s.buffer + total_read, BUFSIZE)) != 0) {
if (nread == -1) {
if (errno == EINTR) {
perror("error EINTR");
continue;
} else {
perror("read() failed in read_file");
close(fd);
return error;
}
} else {
printf("%ld %ld %d\n", total_read, s.size, nread);
total_read += nread;
s.size = total_read;
if (nread == BUFSIZE) {
if (realloc(s.buffer, s.size + BUFSIZE) == NULL) {
perror("out of memory...");
close(fd);
return error;
}
}
}
}
close(fd);
s.buffer[s.size] = 0;
return s;
}
int main()
{
const char* path = "/usr/share/dict/cracklib-small";
string_t s = read_file(path);
if (s.size == -1) {
printf("error\n");
return 1;
}
printf("%s\n", s.buffer);
free(s.buffer);
return 0;
}
运行它会得到以下结果:
0 0 8192
8192 8192 8192
16384 16384 8192
24576 24576 8192
32768 32768 8192
40960 40960 8192
49152 49152 8192
57344 57344 8192
65536 65536 8192
73728 73728 8192
81920 81920 8192
90112 90112 8192
98304 98304 8192
106496 106496 8192
114688 114688 8192
122880 122880 8192
131072 131072 8192
read() failed in read_file: Bad address
error
Valgrind 展示:
==4299== Syscall param read(buf) points to unaddressable byte(s)
==4299== at 0x5184F00: __read_nocancel (in /usr/lib/libc-2.21.so)
==4299== by 0x400A58: read_file (file_helpers.c:31)
==4299== by 0x400AA3: main (file_helpers.c:64)
==4299== Address 0x7568040 is 0 bytes after a block of size 8,192 free'd
==4299== at 0x4C2C29E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4299== by 0x400A12: read_file (file_helpers.c:46)
==4299== by 0x400AA3: main (file_helpers.c:64)
==4299==
我可以看到它抱怨 realloc,但我不明白是什么导致了错误的地址错误。 read() 写入的缓冲区是否在最后一个 realloc() 之后以某种方式损坏?
wc -c 报告运行函数的文件有 477238 个字节。
【问题讨论】:
-
谢谢。这个问题可以作为“如何写一个好问题”的例子。
-
是否有充分的理由不使用
fstat()来确定文件大小、分配空间,然后一次全部读取? -
我正在编写一个爬虫作为一个辅助项目,想看看在不知道响应大小的情况下从套接字读取是如何工作的,所以我想我会先尝试从文件中读取。
-
一些注意事项:1) 表达式'sizeof(char)' 总是返回1,并且对malloc() 的调用没有影响。它只是使代码混乱。建议从对 malloc() 的任何调用中删除该表达式 2) 对 read() 的调用始终要求 BUF_SIZE 字节,但在读取任何字节后,输入缓冲区中没有可用的 BUF_SIZE 字节。建议使用:'while ((nread = read(fd, s.buffer + total_read, BUFSIZE-total_read)) != 0) {'
-
一些注意事项,继续 3) 传递一个完整的结构不是一个好主意,原因有几个。建议传递/返回指向结构的指针。 4) 在成功调用 malloc() 之后,如果出现错误,则需要将指向已分配内存的指针传递给 free()。 5) 对 realloc() 的调用没有保存从 realloc() 返回的指针。结果是 s.buffer 仍然指向旧分配的内存,在分配的内存复制到新分配的内存后,它被释放了。