double num = 1./3 .;
fprintf(fp,"%f", num);
сохраняет num в виде последовательности из восьми символов: 0.333333.
Применение спецификатора %. 2f позволяет сохранить его как последовательность из четырех символов: 0.33. Использование спецификатора %.12f дает возможность сохранить его в виде 14 символов: 0.333333333333. Смена спецификаторов приводит к изменению размера пространства, необходимого для значения. После того, как значение num было сохранено как 0.33, нет никакой возможности вернуться к полной точности значения при его чтении из файла. В общем случае функция fprintf() преобразует числовые значения в символьные данные, возможно изменяя значения.
Наиболее точный и единообразный способ сохранения числа предусматривает использование того же самого набора битов, что и компьютер. Таким образом, значение double должно быть сохранено в области с размером как у типа double. Когда данные хранятся в файле в представлении, которое применяется в программе, мы говорим, что данные сохранены в двоичтй форме. Никакие преобразования из числовых форм в последовательности символов не производятся. Для стандартного ввода-вывода такую услулу предлагают функции fread() и fwrite(), работа которых иллюстрируется на рис. 13.3.
В действительности, как вы помните, все данные хранятся в двоичной форме. Даже символы хранятся с использованием двоичного представления их кодов. Однако если все данные в файле интерпретируются как коды символов, мы говорим, что файл содержит текстовые данные. Если некоторые или все данные интерпретируются как числовые данные в двоичной форме, мы говорим, что файл содержит двоичные данные. (Кроме того, двоичными также являются файлы, в которых данные представляют собой команды на машинном языке.)
Применение терминов двоичный и текстовый может привести к путанице. Стандарт ANSI С распознает два режима открытия файлов: двоичный и текстовый. Многие операционные системы распознают два файловых формата: двоичный и текстовый. Все эти характеристики связаны, но не идентичны. Вы можете открывать файл текстового формата в двоичном режиме. Вы можете сохранять текст в файле двоичного формата. Вы можете использовать функцию getc() для копирования файлов, содержащих двоичные данные. Однако для сохранения двоичных данных в файле двоичного формата вы обычно будете применять двоичный режим. Аналогично, чаще всего работа с текстовыми данными в текстовых файлах производится при их открытии в текстовом режиме. (Файлы, генерируемые текстовыми процессорами, как правило, являются двоичными, поскольку они содержат много нетекстовой информации, которая описывает шрифты и форматирование.)
552 глава 13

Рис. 13.3. Двоичный и текстовый вывод
ФУНКЦИЯ size_t fwrite()
Ниже показан прототип функции fwrite():
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb,
FILE * restrict fp);
Функция fwrite() записывает двоичные данные в файл. Тип size_t определен в терминах стандартных типов С. Это тип, возвращаемый операцией sizeof. Обычно им является unsigned int, но реализации могут выбирать другой тип. Указатель ptr — это адрес порции данных, предназначенной для записи. Аргумент size представляет размер в байтах порции данных, подлежащих записи, a nmemb — количество таких порций. Как обычно, fp идентифицирует файл, в который должна производиться запись. Например, чтобы сохранить объект данных (такой как массив) размером 256 байтов, можно поступить так:
char buffer[256];
fwrite (buffer, 256, 1, fp);
Этот вызов fwrite() записывает одну порцию даниых размером 256 байтов из буфера в файл. Чтобы сохранить, скажем, массив из 10 элементов double, понадобятся следующие операторы:
Файловый ввод-вывод 553
double earnings[10];
fwrite(earnings, sizeof (double), 10, fp);
Этот вызов fwrite() записывает данные из массива earnings в файл 10 порциями данных, каждая из которых имеет размер double.
Возможно, вы обратили внимание на странное объявление const void * restrict ptr в прототипе fwrite(). Проблема, связанная с функцией fwrite(), заключается в том, что ее первый аргумент не имеет фиксированного типа. Скажем, в первом примере использовался аргумент buffer, имеющий тип указателя на char, а во втором примере — аргумент earnings с типом указателя на double. В контексте прототипов ANSI С эти фактические аргументы преобразуются в тип указателя на void, который действует как своего рода универсальный тип для указателей. (До выхода ANSI С для этого аргумента применялся тип char *, требующий приведения к нему актуальных аргументов.)
Читать дальше