Объяснение о функциональности fread

При поиске на этой доске информации о чтении полного файла в память с помощью C я наткнулся на использование fread(), которого раньше не видел. Я пытаюсь это понять.

Мои вопросы:

Есть ли название/термин для того, что здесь делается?

Что происходит, когда size_t used добавляется к char *data, и как фред считает это действительным void *ptr?

Я собираюсь поместить код из поста автора сюда, а также дам ссылку на пост. К сожалению, пост старый, заблокирован, и у меня недостаточно баллов, чтобы оставить комментарий с просьбой разъяснить его.

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

/* Size of each input chunk to be
   read and allocate for. */
#ifndef  READALL_CHUNK
#define  READALL_CHUNK  262144
#endif

#define  READALL_OK          0  /* Success */
#define  READALL_INVALID    -1  /* Invalid parameters */
#define  READALL_ERROR      -2  /* Stream error */
#define  READALL_TOOMUCH    -3  /* Too much input */
#define  READALL_NOMEM      -4  /* Out of memory */

/* This function returns one of the READALL_ constants above.
   If the return value is zero == READALL_OK, then:
     (*dataptr) points to a dynamically allocated buffer, with
     (*sizeptr) chars read from the file.
     The buffer is allocated for one extra char, which is NUL,
     and automatically appended after the data.
   Initial values of (*dataptr) and (*sizeptr) are ignored.
*/
int readall(FILE *in, char **dataptr, size_t *sizeptr)
{
    char  *data = NULL, *temp;
    size_t size = 0;
    size_t used = 0;
    size_t n;

    /* None of the parameters can be NULL. */
    if (in == NULL || dataptr == NULL || sizeptr == NULL)
        return READALL_INVALID;

    /* A read error already occurred? */
    if (ferror(in))
        return READALL_ERROR;

    while (1) {

        if (used + READALL_CHUNK + 1 > size) {
            size = used + READALL_CHUNK + 1;

            /* Overflow check. Some ANSI C compilers
               may optimize this away, though. */
            if (size <= used) {
                free(data);
                return READALL_TOOMUCH;
            }

            temp = realloc(data, size);
            if (temp == NULL) {
                free(data);
                return READALL_NOMEM;
            }
            data = temp;
        }

        n = fread(data + used, 1, READALL_CHUNK, in);
        if (n == 0)
            break;

        used += n;
    }

    if (ferror(in)) {
        free(data);
        return READALL_ERROR;
    }

    temp = realloc(data, used + 1);
    if (temp == NULL) {
        free(data);
        return READALL_NOMEM;
    }
    data = temp;
    data[used] = '\0';

    *dataptr = data;
    *sizeptr = used;

    return READALL_OK;
}

Ссылка: Программирование на C: как прочитать все содержимое файла в буфер

Это очень стандартная техника. Я не уверен, что у него есть какое-то одно имя. Основная идея заключается в том, чтобы вызвать malloc, чтобы выделить немного памяти, начать ее использовать, если у вас закончилось место, вызвать realloc, чтобы увеличить его. При желании, когда вы закончите, если вы перераспределили, вызовите realloc в последний раз, чтобы уменьшить область немного меньше, до именно того, что вам нужно.

Steve Summit 22.04.2022 22:24

В чем именно заключается ваш вопрос? Что делает эта функция? (Попытка прочитать весь файл в память.) Что такое data + used? (Арифметика указателей.) Каков результат этой операции? (char *) Как это можно интерпретировать как действительный void *? (Любой указатель можно преобразовать в void * и обратно.) Куда он указывает? (В конце данные уже считаны.)

DevSolar 22.04.2022 22:24

Поиск "арифметика указателя"

William Pursell 22.04.2022 22:27

Извините, если мой первоначальный вопрос был неясен, но я знаю, что делает код в целом. @DevSolar упомянул, что это арифметика указателей, и теперь я лучше ее понимаю. Плакат увеличивает данные указателя на используемую сумму и считывает следующий фрагмент файла в это место. Это правильно?

brokaryote 22.04.2022 22:30

Вместо того, чтобы использовать realloc() для фрагментов памяти, используйте fstat(), чтобы найти размер файла и выполните malloc() один раз, а затем загрузите файл в память. Таким образом, вы можете заранее ограничить/проверить размер файла без выделения памяти.

जलजनक 22.04.2022 22:39

@SparKot Что делать, если вы читаете из канала или сетевого сокета?

Steve Summit 22.04.2022 22:42

@brokaryote Да, это правильно.

user3386109 22.04.2022 22:46

@SparKot Показанный код является стандартным C. fstat - это POSIX. Это может иметь значение для некоторых.

DevSolar 22.04.2022 23:26
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
8
43
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

What is happening when the size_t used is being added to the char *data and how is this considered a valid void *ptr by fread?

На практике (*) указатель — это просто число, которое ссылается на адрес в (виртуальной) памяти. Здесь делается простая арифметика указателя: вы можете добавить целое число к указателю, что увеличит его значение, поэтому, если ваш указатель указывал на адрес 1000, а вы добавили 20, теперь он указывает на адрес 1020. Поскольку used всегда является числом байтов, прочитанных до сих пор, вы указываете это количество байтов в буфер data.

Но есть еще одна вещь: это работает только так, как описано, если тип данных указателя имеет размер 1 байт (как это делает char(*)). Потому что, когда вы выполняете арифметику указателя, вы не увеличиваете указатель на это количество байтов, а на самом деле кратно размеру типа данных, поэтому вы всегда в конечном итоге указываете на начало элемента в вашем массиве, а не где-то в средний, если вы имеете дело с int. т.е. если у вас есть int *x, который указывает на адрес 1000, и вы делаете x += 20, то x теперь будет указывать на адрес 1080, где x[20] будет находиться.

and how is this considered a valid void *ptr by fread?

Учитывая, что «указатели — это просто числа», fread не волнует, как вы пришли к этому значению указателя. Пока есть допустимая память для записи, она с радостью примет все, что вы ей передадите.

(*) Предполагая современную архитектуру, доступную простым смертным.

Другие вопросы по теме