【问题标题】:Segmentation fault - Trying to read binary file into memory分段错误 - 试图将二进制文件读入内存
【发布时间】:2019-10-03 05:51:17
【问题描述】:

与读取实际文件的成本更高相比,我似乎无法将二进制文件加载到内存中以获得更好的读取性能。该文件为 124 MB,应该能够完全放入内存中。这是 C 语言,由 GCC 6.3 在 84_64 GNU/Linux 上编译。

尝试从fread 访问blk* 时出现问题。

我尝试的两个 malloc 调用是:

uint8_t *blk = malloc(sizeof(uint8_t) * fileSize + 1);
uint8_t *blk = (uint8_t *) malloc(sizeof(uint8_t) * fileSize + 1);

并检查malloc 是否返回NULL,但没有。

        FILE *file = fopen("path", "rb");

        fseek(file, 0, SEEK_END); 
        long fileSize = ftell(file);

        if (ftell(file) != EOF) {
            printf("Ftell not EOF);
        }

        fseek(file, 0, SEEK_SET);

        uint8_t *blk = malloc(sizeof(uint8_t) * fileSize + 1);

        if (file != NULL) {
            printf("File Not NULL and %d\n", fileSize);
        }
        if (blk) {
            printf("Not NULL\n");
        } else {
            printf("NULL\n");
        }

        fread(blk, 1, fileSize, file);
        fclose(file);

        printf("blk: %p | %d\n", *&blk, blk);

输出是:

    Ftell not EOF
    File Not NULL and 134215964
    blk: 0x7fffffffdcc0 | -9024 
    Not NULL
    Segmentation fault

打印格式可能有误,但不会影响分段错误。

【问题讨论】:

  • 您可以比这更深入地检测它。比如,检查fopen 是否成功。检查fseeks 是否成功; ftell 没有返回 EOF(通常为 -1,想想 that 对你的那个小尺寸方程做了什么)。永远不要假设 IO 有效。假设是一切之母......
  • GCC 没有给出关于 uint8_t blk* 的警告或错误?
  • 所有其他部分都可以工作,我很确定错误出现在 malloc 或 fread 行@WhozCraig
  • 我提到的绝对什么都没有。此代码中有六个错误点,实际上只有一个错误点被检测(即使在功能上也没有考虑到这一点;如果在报告同样多的情况下blk 为 NULL,则代码会进入未定义的行为)。需要调试器和/或更好的工具,最好是前者,但如果覆盖范围很广,后者就足够了。
  • 除了fread(blk, 1, 1, file); 只从file 读取一个字节到blk。远远少于fileSize + 1 字节...另外,鉴于您的不确定性,没有必要强制返回malloc,这是不必要的。见:Do I cast the result of malloc?请同时输出ls -al path的结果。

标签: c gcc segmentation-fault malloc fread


【解决方案1】:

如果您还没有弄清楚,您的分段错误是由以下原因引起的:

 printf("blk: %p | %d\n", *&blk, blk);

由于您尝试将blk(指向uint8_t 的指针)打印为整数。参数类型和printf 格式说明符不匹配会调用未定义行为

C11 Standard - 7.21.6.1 The fprintf function(p9)" If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined."

还要注意,在该语句中,在 blk 之前使用 '*&' 是多余的。它只是blk。取消引用指针的地址就是指针本身。您可以使用来自inttypes.h 的正确宽度宏来更正您的语句以打印blk 中的指针地址和第一个字节,例如

printf("blk: %p | 0x%02" PRIx8 "\n", (void*)blk, *blk);

在您的分配中,不需要fileSize + 1,除非您希望使用hack 来肯定nul-terminate blk 所以它可以用作字符串。在某些情况下它可能很方便,但通常不建议这样做。当ftell 返回文件中的字节数时,这就是您需要分配的全部内容,除非您打算在最后添加一些内容。此外,sizeof(uint8_t)sizeof(char) 总是 1 - 它也是多余的,例如,

    if (!(blk = malloc (filesize))) {       /* validate allocation */
        perror ("malloc-blk");
        return 1;
    }

此外,WhozCraig 试图向您传达每个步骤都应该经过验证。通过适当的验证,代码失败的地方没有任何问题。向每个步骤添加验证看起来类似于:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

int main (int argc, char **argv) {

    uint8_t *blk;
    long filesize;
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    if (fseek (fp, 0, SEEK_END) == -1) {    /* validate seek end */
        perror ("fseek-SEEK_END");
        return 1;
    }
    if ((filesize = ftell (fp)) == -1) {    /* validate ftell */
        perror ("ftell-fp");
        return 1;
    }
    if (fseek (fp, 0, SEEK_SET) == -1) {    /* validate seek set */
        perror ("fseek-SEEK_SET");
        return 1;
    }

    if (!(blk = malloc (filesize))) {       /* validate allocation */
        perror ("malloc-blk");
        return 1;
    }

    if (fread (blk, 1, filesize, fp) != (size_t)filesize) { /* validate read */
        perror ("fread-blk");
        return 1;
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    /* do what you need with blk here */
    printf("blk: %p | 0x%02" PRIx8 "\n", (void*)blk, *blk);

    free (blk);
}

注意:用完后别忘了free (blk);

使用/输出示例

对任何文件按原样运行代码将简单地输出blk 的指针地址和文件中的第一个字节为两位十六进制,例如

$ ./bin/rdfileintoblk ../dat/captnjack.txt
blk: 0x17e9240 | 0x54

查看一下,如果您有任何问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-02
    • 1970-01-01
    • 2020-03-17
    • 2013-06-22
    相关资源
    最近更新 更多