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


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

от FREAD (3):

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

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

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

7 78

7 ответов:

это основано на том, как fread есть.

в одной спецификации UNIX говорится

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

fgetc также имеет это примечание:

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

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

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

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

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

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

здесь, позвольте мне исправить эти функции:

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 и что вещи, указанные в стандарте, возможно, не были в оригинальных идеях дизайнеров. Возможно даже, что вещи, сказанные в "языке программирования C" K&R, могут быть не такими, как когда язык был впервые разработан.

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

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

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

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

Я думаю, это потому, что в 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 низких байта элемента, что бы вы сделали?

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

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

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