首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >奇怪的行为()?

奇怪的行为()?
EN

Stack Overflow用户
提问于 2014-03-19 13:31:55
回答 1查看 1.5K关注 0票数 1

因此,我有一个函数,它需要一个偏移量和一个宽度从设备(通常是硬盘)读取。现在,我使用fseeko()和fread()从磁盘读取。不过,我想用更简明扼要的措辞来代替这一点。然而,无论如何,扩展总是从偏移量0读取。

下面是函数的代码:

只是提个醒。我确实有

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define _FILE_OFFSET_BITS 64

在我的密码顶端。我也用同样的结果尝试了pread64()!

对于fread(),这做了我想要的!:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    }
}

,无论“查找点”是什么,它总是从偏移量0读取:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-03-19 22:13:43

启用编译器警告(GCC的-W -Wall),并阅读手册页中的“GLIBC的特性测试宏要求”部分。

这些警告表明您错过了所需的另一个宏定义,man 2 pread手册页告诉您,您还需要的宏定义是

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define _POSIX_C_SOURCE 200809L

要获得GNU库版本2.12或更高版本以正确声明pread()等。

你所看到的行为,是因为你没有这样的声明。

我想告诉大家我是如何理解整个故事的,因为我希望它能准确地告诉您,通过阅读和理解手册页,以及更重要的是,通过启用和处理编译器警告,您可以节省多少精力和精力。

我知道你认为你现在没有时间去做所有的事情(也许稍后,对吧?),但是你错了:当为Linux编写C(或者一般的类似POSIX的系统)时,这是节省时间的最好方法之一。

我自己总是和GCC一起用-W -Wall。这只会使我们更容易找到问题的根源所在。现在,你把注意力集中在最主要的症状上,因为编程不是政治(还没有),它不会给你带来任何好处。

所以,全貌:

fread()返回读取的元素数。在您的示例中,您读取了一个seekwidth字节元素,并验证了一个元素是否正确读取。

pread()返回读取的字节数。第二个代码片段尝试读取从偏移量seekwidth开始的seekpoint字节,但不检查实际读取了多少数据,只检查是否发生错误。

以下是我认为你正在发生的事情:

  1. 您省略了从glibc 2.12开始需要的#define _POSIX_C_SOURCE 200809L声明(在#include <unistd.h>之前),以使pread()函数原型声明。
  2. 您正在编译时没有警告,或者您忽略了“函数pread的隐式声明”警告。
  3. 您正在32位体系结构上编译,或者使用-m32 GCC选项编译32位体系结构。

如果没有函数原型,编译器假设pread()函数的所有参数都是ints -因此是32位--并且只向pread()提供一个32位的seekpoint值。前三个参数( int、指针和size_t)碰巧都是32位,但是第四个参数文件偏移量是64位。(请记住,您使用#define _FILE_OFFSET_BITS 64告诉了您的C库。)

这意味着pread()接收的64位文件偏移量是垃圾。(这取决于字节顺序--小endian或Intel,或大端或Motorola/PowerPC/ARM-以及特定的体系结构二进制接口(ABI)如何将第四个参数传递给函数,值是如何被混淆的。)

在这种情况下,我认为您的目标是32位英特尔体系结构,通常pread()实际接收的值(以乱码表示)大于文件大小,因此pread()返回0:“过去的文件结束,没有更多的数据可读取”。

您的代码忽略了这一点(因为它不是-1),而是假设它成功地读取了数据。您所看到的数据很可能是以前读取的数据--如果缓冲区返回0,pread()不会修改它。

(这种情况也有其他可能的变体;甚至有些情况下,pread()总是接收零作为(混淆的)偏移量。实际上,这并不重要,因为在所有情况下,拥有适当的功能原型都可以解决问题。您也需要检查pread()返回值,因为无法保证它将实际读取所请求的字节数。它经常这样做,是的;但是没有任何保证,所以请不要做出毫无根据的假设。)

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22518536

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文