所以我有一个函数,需要一个偏移量和一个宽度来读取设备(通常是硬盘)。现在我使用fseeko()和fread()来读取磁盘。不过,我喜欢用pread代替它,因为它更简洁。然而,似乎pread总是从偏移量0开始读取,不管是什么。
下面是函数的代码:
只是一些头像。
我确实有
#define _FILE_OFFSET_BITS 64
在我代码的顶端。我也尝试了pread64(),结果相同!
fseeko()和fread(),这就是我想要的!以下内容:
uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
unsigned char buf[seekwidth];
if(fseeko(fp,seekpoint,SEEK_SET)==0) {
if(fread(buf,sizeof buf,1,fp)==1) {
/* do work with retrieved data */
}
else {
printf("ERROR READING AT: %"PRIu64"| WITH VAL WIDTH: %"PRIu64"\n",seekpoint,seekwidth);
return 4;
}
}
else {
printf("ERROR SEEKING AT: %"PRIu64"\n",seekpoint);
return 3;
}
}
pread(),无论“seekpoint”是什么,它始终从偏移量0读取:
uint8_t retrievedata(FILE *fp,uint64_t seekpoint, uint64_t seekwidth) {
unsigned char buf[seekwidth]
if (pread(fileno(fp),buf,seekwidth,seekpoint)!=-1) {
/* do something */
} else {
printf("ERROR SEEKING AND/OR READING AT: %"PRIu64"\n",seekpoint);
return 3;
}
}
最佳答案
启用编译器警告(-W -Wall
用于gcc),并阅读"Feature Test Macro Requirements for GLIBC"中的man pages部分。
这些警告表明您遗漏了所需的另一个宏定义,而man 2 pread
手册页告诉您还需要的宏定义是
#define _POSIX_C_SOURCE 200809L
使gnu c库版本2.12或更高版本正确声明
pread()
等。你所看到的行为,是由于没有声明。
我想告诉你我是如何理解整个故事的,因为我希望它能告诉你,通过阅读和理解man pages,以及更重要的是,通过启用和处理编译器警告,你可以节省多少汗水和精力。
我知道你觉得你现在没时间做这些(也许以后,对吧?),但您错了:这是为linux(或一般类似posix的系统)编写c时节省时间的最佳方法之一。
我自己总是在gcc中使用
-W -Wall
。这只会让我们更容易找到问题的根源。现在,你把注意力放在了主要症状上,因为编程还不是政治问题,它不会给你带来任何好处。所以,全貌:
fread()
返回读取的元素数。在您的例子中,您读取一个seekwidth
字节的元素,并验证是否正确读取了一个元素。pread()
返回读取的字节数。第二个代码片段尝试从偏移量开始读取seekwidth
字节,但不检查实际读取了多少数据,只检查是否发生错误。以下是我认为发生在你身上的事情:
自glibc 2.12以来,您已经省略了
seekpoint
声明(在#define _POSIX_C_SOURCE 200809L
之前),以获得声明的#include <unistd.h>
函数原型。编译时没有警告,或者忽略“implicit declaration of function`pread`”警告
您正在32位体系结构上编译,或者使用
pread()
gcc选项编译到32位体系结构如果没有函数原型,编译器假设
-m32
函数的所有参数都是int,因此是32位的,并且只向pread()
提供32位的seekpoint
值。前三个参数(int、指针和size)碰巧都是32位的,但是第四个参数file offset是64位的。(请记住,您是这样告诉您的c库的,使用pread()
)这意味着接收到的64位文件偏移量是垃圾。(这取决于字节顺序——小端字节或类似英特尔的字节,大端字节或类似摩托罗拉/PowerPC/ARM的字节——以及特定体系结构二进制接口(ABI)如何将第四个参数传递给函数,确切地说,该值是如何被混淆的。)
在本例中,我相信您的目标是32位Intel体系结构,通常
#define _FILE_OFFSET_BITS 64
实际接收的值(如乱码)是正的,并且大于文件大小,因此pread()
返回0:“文件结束后,不再读取数据”。您的代码忽略了这一点(因为它不是-1),而是假设它成功地读取了数据。很可能您看到的数据来自先前的读取--
pread()
如果返回0,则不会修改缓冲区。(这种情况也有其他可能的变体;甚至有些
pread()
总是收到零作为(混乱的)偏移量。实际上这并不重要,因为在所有情况下,拥有合适的函数原型可以解决问题。您也需要检查pread()
返回值,因为不能保证它会实际读取您请求的字节数。它经常这样做,是的;但是没有保证,所以请不要做没有根据的假设。)