Какое обоснование для fread/fwrite принимает размер и считается аргументом?

У нас была дискуссия на работе о том, почему fread и fwrite принимают размер для каждого члена и подсчитывают и возвращают число членов, читаемых/написанных, вместо того, чтобы просто брать буфер и размер. Единственное, что мы могли бы придумать, - это если вы хотите читать/писать массив структур, которые неравномерно разделены выравниванием платформы и, следовательно, были дополнены, но это не может быть так распространено, чтобы гарантировать этот выбор в дизайне.

Из FREAD (3):

Функция fread() считывает nmemb элементы данных, каждый размер байтов длиной, из потока, на который указывает поток, сохраняя их в указанном месте по ptr.

Функция fwrite() записывает nmemb элементы данных, каждый размер байтов долго, к потоку, на который указывает поток, получая их из местоположения заданный ptr.

fread() и fwrite() возвращают количество элементов, успешно прочитанных или написанных (т.е. не количество символов). Если возникает ошибка или конец файла достигнут, возвращаемое значение - короткое количество элементов (или ноль).

Ответ 1

Он основан на том, как fread.

В спецификации Single UNIX указано

Для каждого объекта вызовы размера должны быть для функции fgetc() и результаты сохраняются в порядке чтения в массив без знака char точно наложение объекта.

fgetc также имеет следующее примечание:

Так как fgetc() работает с байтами, чтение символа, состоящего из несколько байтов (или "многобайтовый character" ) может потребоваться несколько вызовов to fgetc().

Конечно, это предшествует фантазийным кодировкам с переменными байтами, такими как UTF-8.

SUS отмечает, что это фактически взято из документов ISO C.

Ответ 2

Разница в fread (buf, 1000, 1, stream) и fread (buf, 1, 1000, stream) заключается в том, что в первом случае вы получаете только один фрагмент из 1000 байтов или nuthin, если файл меньше и во втором случае вы получите все в файле меньше и до 1000 байт.

Ответ 3

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

Многие файловые системы были основаны на записи, поэтому для эффективного использования таких файловых систем вам нужно будет указать количество элементов ( "записи" ), позволяя fwrite/fread работать на хранилище в виде записей, а не только байтовых потоков.

Ответ 4

Здесь я могу исправить эти функции:

size_t fread_buf( void* ptr, size_t size, FILE* stream)
{
    return fread( ptr, 1, size, stream);
}


size_t fwrite_buf( void const* ptr, size_t size, FILE* stream)
{
    return fwrite( ptr, 1, size, stream);
}

Что касается обоснования параметров fread()/fwrite(), я давно потерял копию K & R, поэтому могу только догадываться. Я думаю, что вероятным ответом является то, что Керниган и Ричи, возможно, просто подумали, что выполнение двоичного ввода-вывода будет наиболее естественно выполняться на массивах объектов. Кроме того, они, возможно, думали, что блок ввода-вывода будет быстрее/проще реализовать или что-то еще на некоторых архитектурах.

Несмотря на то, что в стандарте C указано, что fread() и fwrite() реализованы в терминах fgetc() и fputc(), помните, что стандарт появился задолго до того, как C был определен K & R и что указанные вещи в стандарте, возможно, не было в оригинальных идеях дизайнеров. Даже возможно, что все сказанное в K & R "Язык программирования C" может отличаться от того, когда язык был впервые разработан.

Наконец, вот что говорит П. Дж. Плаугер о fread() в "Стандартной библиотеке C":

Если аргумент size (второй) больше единицы, вы не можете определить будет ли функция читать до size - 1 дополнительных символов, кроме того, что она сообщает. Как правило, вам лучше не использовать функцию как fread(buf, 1, size * n, stream); вместо fread(buf, size, n, stream);

Баскиально, он говорит, что интерфейс fread() нарушен. Для fwrite() он отмечает, что "Ошибки записи, как правило, редки, поэтому это не главный недостаток" - выражение, с которым я бы не согласился.

Ответ 5

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

Ответ 6

Я думаю, что это потому, что C не имеет функции перегрузки. Если бы их было немного, размер был бы лишним. Но в C вы не можете определить размер элемента массива, вы должны указать его.

Рассмотрим это:

int intArray[10];
fwrite(intArray, sizeof(int), 10, fd);

Если fwrite приняло количество байтов, вы можете написать следующее:

int intArray[10];
fwrite(intArray, sizeof(int)*10, fd);

Но это просто неэффективно. У вас будет sizeof (int) раз больше системных вызовов.

Еще один момент, который следует учитывать, заключается в том, что вы обычно не хотите, чтобы часть элемента массива была записана в файл. Вы хотите целое число или ничего. fwrite возвращает несколько элементов, успешно записанных. Итак, если вы обнаружите, что записано только 2 младших байта элемента, что бы вы сделали?

В некоторых системах (из-за выравнивания) вы не можете получить доступ к одному байту целого числа без создания копии и переключения.

Ответ 7

Наличие отдельных аргументов для размера и подсчета может быть выгодным для реализации, которая может избежать чтения каких-либо частичных записей. Если бы кто-то использовал однобайтовые чтения из чего-то вроде трубы, даже если бы кто-то использовал данные в формате фиксированного формата, нужно было бы позволить возможность записи записи на два чтения. Если бы вместо этого можно было запрашивать, например, неблокируемое чтение до 40 записей по 10 байт каждый, когда доступно 293 байта, и система возвращает 290 байтов (29 целых записей), оставляя 3 байта готовыми к следующему чтению, что будет намного удобнее.

Я не знаю, в какой мере реализация fread может обрабатывать такую семантику, но они, безусловно, могут быть полезны для реализаций, которые могут обещать их поддерживать.