所以我有一个函数,需要一个偏移量和一个宽度来读取设备(通常是硬盘)。现在我使用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()返回值,因为不能保证它会实际读取您请求的字节数。它经常这样做,是的;但是没有保证,所以请不要做没有根据的假设。)

09-25 17:59