Ошибка сегментации - Попытка чтения двоичного файла в память - PullRequest
0 голосов
/ 16 мая 2019

Я не могу загрузить двоичный файл в память для лучшей производительности чтения по сравнению с более дорогим чтением к реальному файлу. Размер файла составляет 124 МБ, и он должен полностью умещаться в памяти. Это в C, скомпилированном GCC 6.3 на 84_64 GNU / Linux.

Что-то идет не так при попытке доступа к blk* из fread.

Я попробовал два вызова 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

Формат печати может быть неправильным, но это не должно иметь значения для ошибки сегментации.

1 Ответ

2 голосов
/ 16 мая 2019

Если вы еще не поняли, ошибка сегментации вызвана:

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

Из-за вашей попытки вывести blk (указатель на uint8_t) как целое число . Несоответствие между типами аргументов и спецификатором формата printf вызывает неопределенное поведение .

Стандарт C11 - 7.21.6.1 Fprintf функция (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. Разыменование адреса указателя - это просто указатель. Вы можете исправить свое утверждение, чтобы напечатать адрес указателя и первый байт в blk, используя правильные макросы точной ширины из inttypes.h, например,

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

В вашем распределении нет необходимости в fileSize + 1, если только вы не собираетесь использовать хак для утвердительного ответа 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

Посмотрите вещи и дайте мне знать, если у вас есть какие-либо вопросы.

...