Как на самом деле работает fread в C?

Я понимаю, что fread() имеет следующее определение функции:

size_t fread(void *buffer, size_t size, size_t qty, FILE *inptr);

Я также понимаю, что inptr — это указатель файла, который возвращается, когда указатель FILE открывается с помощью функции fopen(). Мой вопрос: хранит ли inptr адрес памяти каждого символа/буквы файла в своей памяти? Если это так, адреса памяти из inptr копируются в *buffer (указатель на буферный массив)?

Есть еще одна вещь, которая меня смущает. При каждом вызове fread() копируется/передается size * qty байт памяти. Это содержимое файла, на которое указывает сам inptr, или это адрес памяти содержимого файла, который копируется/переносится?

Был бы признателен, если бы кто-нибудь помог мне устранить путаницу. Спасибо :)


person Prav    schedule 13.05.2018    source источник
comment
Мой вопрос: хранит ли inptr адрес памяти каждого отдельного символа/буквы файла в своей памяти? проверьте, что содержит FILE, прочитайте члены этой структуры. откройте vi /usr/include/libio.h и найдите определение FILE.   -  person Achal    schedule 13.05.2018
comment
Указатель файла представляет собой дескриптор, который используется функциями чтения, такими как fread(), для извлечения данных из фактического файла (конечно, при условии, что он был успешно открыт). Реализация функций чтения может использовать буфер, т.е. они копируют часть данных в память, чтобы оптимизировать доступ к фактическому файлу. Но такого рода вещи являются деталями реализации, о которых вам не нужно беспокоиться. И разыменование указателя файла для доступа к памяти — это не способ извлечения данных из файла.   -  person Peter    schedule 13.05.2018
comment
Взгляните на этот ответ: stackoverflow.com/a/5130577/946835   -  person CoyBit    schedule 13.05.2018
comment
и прочитайте этот ответ qr.ae/TUTFQL   -  person CoyBit    schedule 13.05.2018


Ответы (4)


FILE реализуется вашей операционной системой. Функции, работающие на FILE, реализуются вашей системой. Вы не знаете. Чтобы узнать это, вам нужно просмотреть исходные коды вашей операционной системы.
inptr может быть указателем на память, выделенную вашей операционной системой. Или это может быть число, которое ваша операционная система использует для поиска своих данных. В любом случае, это дескриптор, который ваша система использует для поиска FILE конкретных данных. И ваша система решает, что находится в этих данных. В целях кэширования, возможно, все письма кэшируются в каком-то буфере. Возможно, нет.
fread вызов. Fread считывает данные из базовой сущности за дескриптором inptr. inptr интерпретируется вашей системой для доступа к базовой памяти, структуре, устройству, жесткому диску, принтеру, клавиатуре, мыши или чему-то еще. Он читает qty*size байт данных. Эти данные помещаются в файл buffer. Никаких указателей там не ставится. Байты, которые считываются с устройства, помещаются в память, на которую указывает buffer.

person KamilCuk    schedule 13.05.2018

Ваши вопросы немного сбивают с толку (вероятно, поэтому вы их задаете), поэтому я постараюсь ответить.

FILE *inptr — это дескриптор открытого файла. Вы не читаете его напрямую, он просто используется, чтобы сообщить связанным функциям, над чем работать. Вы можете думать об этом как о человеке, читающем имя файла в папке, где имя файла используется для идентификации файла, но доступ к содержимому осуществляется другим способом.

Что касается данных, то они считываются из файла, который открывается с помощью fopen() и впоследствии предоставляет дескриптор файла. Данные не связаны напрямую с указателем FILE, и, как правило, вам не следует напрямую возиться с указателем FILE (не пытайтесь читать/записывать из него напрямую).

Я старался не вдаваться в подробности в отношении операции, так как кажется, что вы новичок в C, но просто думал о FILE * как о способе компьютера «называть» файл внутри для собственного использования, а буфер данных просто содержание.

person Katya Veselnitskaya    schedule 13.05.2018
comment
Могу я спросить, почему я не должен напрямую возиться с указателем FILE? - person Prav; 13.05.2018
comment
@PravElan Три причины: вы не должны, вы не можете и это может не сработать. (1) Все данные, на которые указывает указатель FILE *, являются частными для вашей системы. Вы не предназначены для проверки или манипулирования им напрямую. (2) Ваш код может не иметь доступа к определению структуры FILE, которая позволила бы вам даже попытаться получить доступ к данным. (3) указатели FILE * являются специальными; существуют особые правила для вещей, которые вам не разрешено делать с ними. (Правила позволяют определенным реализациям работать так, как они работают. Но они довольно неясны, и вы вряд ли нарушите их случайно.) - person Steve Summit; 13.05.2018

Вы можете думать о fread как о реализации примерно так:

size_t fread(char *ptr, size_t size, size_t nitems, FILE *fp)
{
    size_t i;
    for(i = 0; i < size * nitems; i++) {
        int c = getc(fp);
        if(c == EOF) break;
        *ptr++ = c;
}

(Я не упомянул возвращаемое значение, потому что на моей упрощенной иллюстрации нет хорошего способа показать его.)

Другими словами, fread считывает набор символов, как если бы многократно вызывал getc(). Очевидно, возникает вопрос о том, как работает getc.

Что вам нужно знать, так это то, что FILE * указывает на структуру, которая так или иначе содержит буфер некоторых (не обязательно всех) символов файла, считанных в память. Итак, в псевдокоде getc() выглядит так:

int getc(FILE *fp)
{
    if(fp->buffer is empty) {
        fill fp->buffer by reading more characters from underlying file;
        if(that resulted in end-of-file)
            return EOF;
    }

    return(next character from fp->buffer);
}
person Steve Summit    schedule 13.05.2018

Ответ на вопрос,

"как работает fread()?"

в основном

«он просит вашу операционную систему прочитать файл для вас».

Более или менее единственной целью ядра операционной системы является выполнение подобных действий от вашего имени. Ядро содержит драйверы устройств для дисков и файловых систем и может извлекать данные для вашей программы независимо от того, на чем хранится файл (например, на жестком диске, отформатированном в FAT32, в сетевой папке и т. д.).

Способ, которым функция fread() запрашивает вашу операционную систему для извлечения данных из файла, немного различается между ОС и процессором. В старые добрые времена MS-DOS функция fread() загружала различные параметры (вычисляемые из параметров, которые ваша программа передала функции fread()) в регистры процессора, а затем вызывала прерывание. Обработчик прерываний, который фактически был частью MS-DOS, затем брал запрошенные данные и помещал их в заданное место в памяти. Загружаемые регистры и вызываемое прерывание были указаны в руководствах MS-DOS. Параметры, которые вы передаете функции fread(), являются абстракциями тех, которые необходимы системному вызову.

Это то, что известно как системный вызов. Каждая операционная система имеет интерфейс системных вызовов. Библиотеки, такие как glibc в Linux, предоставляют удобные функции, такие как fread() (которая является частью стандартной библиотеки C), и выполняют за вас системный вызов (который не стандартизирован между операционными системами).

Обратите внимание, что это означает, что glibc не является фундаментальной частью операционной системы. Это просто библиотека подпрограмм, которая реализует стандартную библиотеку C вокруг системных вызовов, предоставляемых Linux. Это означает, что вы можете использовать альтернативную библиотеку C. Например, Android не использует glibc, несмотря на то, что у него есть ядро ​​Linux.

Аналогично на винде. Все программное обеспечение в Windows (C, C++, среда выполнения .NET и т. д.) написано для использования библиотеки WIN32 API (win32.dll). Отличие в Windows состоит в том, что интерфейс системных вызовов ядра NT не публикуется; мы не знаем, что это такое.

Это приводит к некоторым интересным вещам.

  • WINE в Linux воссоздает WIN32.dll, а не интерфейс системных вызовов ядра NT.
  • Подсистема Windows для Linux в Windows 10 воссоздает интерфейс системных вызовов Linux (что возможно, поскольку это общеизвестно).
  • Solaris, QNX и FreeBSD проделывают тот же трюк.
  • Еще более странно выглядит то, что MS сделала прокладку системного интерфейса ядра NT для Linux (то есть то, чего не сделала WINE), чтобы позволить MS-SQLServer работать в Linux. Фактически это подсистема Linux для Windows. Они не отдали это.
person bazza    schedule 13.05.2018