Учебная деятельность    

STDIO - Standard Input/Output Library

По мотивам А.П.Полищук, С.А.Семериков "Системное программирование в UNIX средствами Free Pascal"

StdIO - стандартная библиотека ввода-вывода, основная цель которой состоит в предоставлении эффективных, развитых и переносимых средств доступа к файлам. Библиотека предлагает набор функций для форматированного ввода, вывода и преобразования данных. Процедуры стандартного ввода-вывода являются переносимыми, так как они не привязаны к особым свойствам той или иной платформы, а являются частью группы стандартов POSIX. Любой полноценный компилятор языка С предоставляет доступ к стандартной библиотеке ввода-вывода независимо от используемой операционной системы. Компилятор Free Pascal также позволяет использовать эту библиотеку в рамках портирования библиотечных функций языка Си (модуль LibC).

Процедуры буферизованного ввода-вывода из библиотеки STDIO идентифицируют открытые файлы при помощи указателя на структуру типа FILE, содержащую описательную информацию о файле (дескриптор файла). В Free Pascal указатель на FILE объявляется типом PFILE. Переменные этого типа имеют назначение, аналогичное назначению переменных файлового типа в традиционном Паскале. Чтобы начать работу с файлом, его надо открыть при помощи функции fopen. Первым параметром этой функции указывается имя файла, вторым - строка, обозначающая режим доступа:

Если функция fopen завершается неудачей, она возвращает указатель nil. Следует отметить, что для файлов, открытых в режиме обновления (строка режима содержит '+'), программа может одновременно выполнять и операции ввода, и операции вывода без необходимости открывать этот файл заново. При этом возможно перемещение внутреннего указателя чтения-записи при помощи функций rewind (к началу файла) и fseek (в произвольное место). Третий параметр функции fseek задает начальную точку, от которой отсчитывается новое положение указателя чтения-записи: SEEK_SET - от начала файла, SEEK_CUR - от текущего положения указателя, SEEK_END - от конца файла. Определить текущее положение указателя чтения-записи - при помощи функции ftell. Чтобы закрыть файл, используется функция fclose.
uses StdIO;

function fopen(filename:pchar; mode:pchar):pfile;
function fclose(fp:pfile):integer;
function fseek(fp:pfile; offset:longint; direction:integer):longint;
procedure rewind(fp:pfile);
function ftell(fp:pfile):longint;
#include <stdio.h>

FILE *fopen(const char *filename, const char *mode);
int fclose(FILE *fp);
int fseek(FILE *fp, long offset, int direction);
void rewind(FILE *fp);
long ftell(FILE *fp);

При запуске любой программы ей доступны три открытых файла: стандартный ввод, стандартный вывод и диагностический вывод (вывод ошибок). Обычно все три файла связаны с терминалом (экран+клавиатура), с которого запущена программа. Однако пользователь может и перенаправить любой из этих файлов при помощи средств командного интерпретатора. Программе не требуется специально открывать эти файлы, библиотека STDIO предоставляет к ним доступ через предопределенные указатели: stdin, stdout, stderr.

Для побайтного (посимвольного) ввода-вывода используются функции putc и getc. Функция getc возвращает очередной байт (символ) из указанного входного потока. Функция putc помещает байт (символ), переданный первым параметром, в выходной поток, указанный вторым параметром. В случае ошибки эти функции возвращают значение EOF (-1). Функция getc также возвращает EOF при достижении конца файла. Кроме того, конец файла можно обнаружить при помощи функции feof. Эта функция возвращает ненулевое значение, если в результате последней операции чтения был достигнут конец файла (т.е. для того, чтобы feof вернула корректный результат, надо попытаться прочитать или записать файл).
function getc(fp:pfile):integer;
function putc(c:integer; fp:pfile):integer;
function feof(fp:pfile):integer;
int getc(FILE *fp);
int putc(int c, FILE *fp);
int feof(FILE *fp);

В приведенном ниже примере программа копирует символы, вводимые со стандартного ввода (клавиатуры), в указанный файл (файл задается первым параметром командной строки программы):
program ex1;
 uses StdIO;
var  fout:pfile;
 c:integer;
 filename:string;

begin
 if ParamCount<1 then begin
  fprintf(stderr,'Missing command-line parameter'#10,[]);
  halt(1);
 end;
 filename:=ParamStr(1);
 fout:=fopen(@filename[1],'w');
 if fout=nil then begin
  fprintf(stderr,'Failed to open file'#10,[]);
  halt(1);
 end;
 repeat
  c:=getc(stdin);
  if c=EOF then break;
  putc(c,fout);
 until false;
 fclose(fout);

end.
#include <stdio.h>

FILE *fout;
int c;
char *filename;

int main(int argc, char** argv) {
 if (argc<2) {
  fprintf(stderr,"Missing command-line parameter\n");
  return 1;
 }
 filename=argv[1];
 fout=fopen(filename,"w");
 if (!fout) {
  fprintf(stderr,"Failed to open file\n");
  return 1;
 }
 do {
  c=getc(stdin);
  if (c==EOF) break;
  putc(c,fout);
 } while(1);
 fclose(fout);
 return 0;
}

Т.к. ввод и вывод через потоки stdin и stdout используется очень часто, для удобства определены две дополнительных функции побайтного ввода-вывода специально для работы с этими файлами: getchar соответствует getc(stdin), а putchar соответствует putchar(c,stdout).
function getchar:integer;
function putchar(c:integer):integer;
int getchar(void);
int putchar(int c);
 В Си эти функции, как правило, реализованы в виде макроопределений.

Описываемые здесь функции ввода-вывода относятся к так называемому буферизированному вводу-выводу. Это означает, что с каждым файлом связана некоторая область данных, называемая буфером. Фактическое обращение к носителю для чтения или записи происходит блоками большого размера для обеспечения эффективности. Так, например, если программа записывает данные в файловый поток побайтно, то на самом деле данные попадают на диск только тогда, когда заполнен буфер записи (или файл закрывается). Выполнение функции fflush приводит к сбросу на диск содержимого буфера вывода, связанного с указанным потоком. Другими словами, данные из буфера записываются в файл немедленно, независимо от того, заполнен буфер или нет. Это гарантирует,что содержимое файла на диске будет соответствовать тому, как он выглядит с точки зрения процесса. (Процесс считает, что данные записаны в файл с того момента, как они оказываются в буфере, поскольку механизм буферизации прозрачен.) Любые данные из буфера ввода этим вызовом предусмотрительно отбрасываются. Поток остается открытым после завершения процедуры fflush. Функция fflush возвращает EOF в случае ошибки и нулевое значение - в случае успеха.
function fflush(fp:pfile):integer;
int fflush(FILE *fp);

Использование fflush наиболее актуально при работе с текстовыми файлами, т.к. механизм буферизации откладывает запись в такой файл до получения символа конца строки (или до достижения конца буфера). Если приведенный выше пример модифицировать для копирования одного файла в другой, причем снабдить его прогрессом копирования (например, через каждые 1000 байт выводить символ *), то использование fflush становится обязательным, т.к. в противном случае звездочки прогресса будут выведены не по ходу копирования, а когда программа завершится или будет заполнен буфер.
program ex1;
 uses StdIO;
 
var fin,fout:pfile;
 c:integer;
 filename1,filename2:string;
const count:longint=0;

begin
 if ParamCount<2 then begin
  fprintf(stderr,'Missing command-line parameters'#10,[]);
  halt(1);
 end;
 filename1:=ParamStr(1);
 fin:=fopen(@filename1[1],'r');
 filename2:=ParamStr(2);
 fout:=fopen(@filename2[1],'w');
 if (fin=nil) or (fout=nil) then begin
  fprintf(stderr,'Failed to open files'#10,[]);
  halt(1);
 end;
 repeat
  c:=getc(fin);
  if c=EOF then break;
  putc(c,fout);
  inc(count);
  
  if count mod 1000 = 0 then begin
   putchar(ord('*'));
   fflush(stdout);
  end;
 until false;
 fclose(fin);
 fclose(fout);

end.
#include <stdio.h>
#include <unistd.h>

FILE *fin,*fout;
int c;
char *filename1,*filename2;
long count=0;

int main(int argc, char** argv) {
 if (argc<3) {
  fprintf(stderr,"Missing command-line parameters\n");
  return 1;
 }
 filename1=argv[1];
 fin=fopen(filename1,"r");
 filename2=argv[2];
 fout=fopen(filename2,"w");
 if (!fin || !fout) {
  fprintf(stderr,"Failed to open files\n");
  return 1;
 }
 do {
  c=getc(fin);
  if (c==EOF) break;
  putc(c,fout);
  usleep(1000);

  if (++count % 1000 == 0) {
   putchar('*');
   fflush(stdout);
  }
 } while(1);
 fclose(fin);
 fclose(fout);
 return 0;
}

Существует также набор простых процедур для ввода и вывода строк (под которыми понимается последовательность символов, завершаемая символом перевода строки). Эти процедуры удобно использовать в интерактивных программах, которые выполняют чтение с клавиатуры и вывод на экран терминала.
function gets(buf:pchar):pchar;
function fgets(buf:pchar; nsize:integer; fp:pfile):pchar;
function puts(str:pchar):integer;
function fputs(str:pchar; fp:pfile):integer;
char *gets(char *buf);
char *fgets(char *buf, int nsize, FILE *fp);
int puts(const char *str);
int fputs(const char *str, FILE *fp);

Основные функции для ввода строк называются gets и fgets. Функция gets считывает последовательность символов из потока стандартного ввода (stdin), помещая все символы в буфер, на который указывает аргумент buf. Символы считываются до тех пор, пока не встретится символ перевода строки или конца файла. Символ перевода строки отбрасывается, а вместо него в буфер buf помещается нулевой символ, образуя завершенную строку. В случае возникновения ошибки или при достижении конца файла возвращается значение nil. Функция fgets является обобщенной версией функции gets. Она считывает символы из указанного потока в буфер buf до тех пор, пока не будет считано nsize-1 символов или не встретится раньше символ перевода строки или не будет достигнут конец файла. В функции fgets символы перевода строки не отбрасываются, а помещаются в конец буфера. Обе функции возвращают указатель на буфер buf в случае успеха и nil - в противном случае. Предпочтительным является использование функции fgets даже для файла стандартного ввода (stdin), т.к. при использовании функции gets не существует способа указать размер буфера для строки. Использование функции gets может привести к ошибкам типа "переполнение буфера".

Для вывода строк используются функции puts и fputs. Функция puts записывает все символы (кроме завершающего нулевого символа) из строки str на стандартный вывод (stdout). Для обеспечения совместимости со старыми версиями системы функция puts добавляет в конце символ перевода строки. Функция fputs записывает строку str в указанный поток (функция fputs не добавляет перевод строки, если он явно не указан в строке). Обе функции возвращают в случае ошибки значение EOF.

Приведенный ниже фрагмент программы содержит описание функции, возвращающей количество строк в указанном файле, при условии, что строки файла содержат не более 255 символов.
function fcountlines(filename:pchar):integer;
 var f:pfile; 
 buf:array [0..255] of char;
 const cnt:integer=0;
begin
 f:=fopen(filename,'r');
 if f=nil then begin fcountlines:=-1; exit; end;
 while fgets(buf,256,f)<>nil do inc(cnt);
 fclose(f);
 fcountlines:=cnt;
end;
int fcountlines(const char *filename)
{
 FILE *f;
 char buf[256];
 int cnt=0;
 f=fopen(filename,"r");
 if (!f) return -1;
 while (fgets(buf,256,f)) cnt++;
 fclose(f);
 return cnt;
}

Ввод и вывод произвольных бинарных (нетекстовых) данных осуществляется функциями fread и fwrite. Функция fread считывает nitems объектов данных из входного файла. Считанные байты будут помещены в массив buf. Каждый считанный объект представляется последовательностью байтов длины size. Возвращаемое значение дает число успешно считанных объектов. Функция fwrite записывает данные из массива buf в указанный поток. Массив buf содержит nitems объектов, размер которых равен size. Возвращаемое функцией значение дает число успешно записанных объектов. Эти функции обычно используются для чтения и записи содержимого произвольных структур данных. При этом параметр size часто содержит конструкцию sizeof(...), которая возвращает размер структуры в байтах.
function fread(buf:pointer; size, nitems:longint; fp:pfile):longint;
function fwrite(buf:pointer; size, nitems:longint; fp:pfile):longint;
size_t fread(void *buf, size_t size, size_t nitems, FILE *fp);
size_t fwrite(const void *buf, size_t size, size_t nitems, FILE *fp);

Библиотека STDIO предоставляет средства форматированного вывода (преобразования информации в текстовый вид). Функции printf, fprintf и sprintf преобразуют значения аргументов args в строку в соответствии с параметром fmt (формат). Тип и количество аргументов определяются строкой формата. Функция printf выдает сформированную строку на стандартный вывод, fprintf - в указанный файловый поток, а sprintf - в строковой буфер str.
function printf(fmt:pchar; args:array of const):integer;
function fprintf(fp:pfile; fmt:pchar; args:array of const):integer;
function sprintf(str:pchar; fmt:pchar; args:array of const):integer;
int printf(const char *fmt, ...);
int fprintf(FILE *fp, const char *fmt, ...);
int sprintf(char *str, const char *fmt, ...);

Примеры вызова функции printf:
printf('Hello, world!'#10, []);
printf('hex=%x, oct=%o, dec=%d'#10, [i, i, i]);
printf("Hello, world!\n");
printf("hex=%x, oct=%o, dec=%d\n",i,i,i);

Строка формата fmt состоит из обычных символов, которые копируются без изменений, и набора спецификаций формата. Это подстроки, которые начинаются с символа % (если нужно напечатать сам символ процента, то он записывается два раза: %%). Для каждого из аргументов args должна быть задана своя спецификация формата, которая указывает тип соответствующего аргумента и способ его преобразования в текстовый вид. Первый пример просто выводит строку 'Hello, world!' и переводит курсор на новую строку (символ с кодом #10 - перевод строки). Поскольку в этом примере нет других аргументов, кроме строки формата, эта строка не содержит ни одной спецификации формата. Второй пример выводит значение целочисленной переменой i в шестнадцатеричном, восьмеричном и десятичном виде. Поскольку значение переменной i используется 3 раза, строка fmt содержит три спецификации формата: '%x', '%o' и '%d'. Если, например, переменная i будет содержать значение 1710, то на стандартный вывод будет выдана строка:

hex=11, oct=21, dec=17

Спецификация формата может содержать от 1 до 5 полей:

%[FLAGS][WIDTH][.PREC][SIZE]TYPE
Обязательным является поле TYPE, определяющее тип и способ преобразования соответствующего аргумента. Поле TYPE кодируется одним из следующих символов:

Остальные поля необязательны и могут остутствовать. Так, например, поле SIZE уточняет тип аргумента и может принимать следующее значение:

Если используется поле PREC, оно должно предваряться точкой. Для целых чисел это поле задает минимальное количество выводимых цифр (при необходимости слева будут добавлены нули). Для вещественных форматов (f, e, E) это поле задает количество цифр, выводимых после десятичной точки, для g и G - количество значимых цифр.

Поле WIDTH определяет минимальное количество выводимых символов. При необходимости результат преобразования будет дополнен пробелами.

Поля WIDTH и PREC вместо числа могут содержать '*', тогда в аргументах функции в соответствующем месте должен присутствовать целочисленный аргумент, значение которого и будет использоваться как параметр преобразования.

Поле FLAGS определяет дополнительные параметры преобразования (можно использовать несколько флагов одновременно):

Примеры спецификаций:
Спецификация Аргументы Результат
%04X i:integer=10
000A
j:integer=1000
03E8
%.3f x:real=3.141592
3.142
y:real=0.5
0.500
%+8.3G x:real=3.141592
   +3.14
y:real=-0.00005678
-5.68E-05
%.*Le n:integer=2; x:extended=0.5
5.00e-01
m:integer=3; y:extended=1
1.000e+00
%-*c%s n:integer=3; c:char='A'; s:pchar='qwerty'
A  qwerty
m:integer=2; a:integer=64; s:pchar='mail.ru'
@ mail.ru

Еще один комплексный пример:
program ex2;
 uses StdIO;
var m:integer;
const n:integer=3;
 x:real=1;
begin
 printf('x=%.*E%n',[n,x,@m]);
 printf(' [%d]'#10,[m]);
end.
#include <stdio.h>

int n=3,m;
float x=1;

int main() {
 printf("x=%.*E%n",n,x,&m);
 printf(" [%d]\n",m);
}

В результате на стандартный вывод будет выдана строка:

x=1.000E+00 [11]
В первом вызове printf использовано две спецификации вывода (символы 'x=' выдаются "как есть"). Первая спецификация вывода требует два аргумента: целое число, определяющее количество цифр после запятой, и вещественное число, которое, собственно, и выдается на стандартный вывод в экспоненциальном формате. Вторая спецификация не генерирует никаких символов, а лишь помещает в целочисленную переменную, адрес которой передается очередным аргументом функции printf, количество сгенерированных к этому моменту символов. Второй вызов printf содержит лишь одну спецификацию вывода - вывод целого числа, остальные символы строки формата (пробел, квадратные скобки и перевод строки) выводятся "как есть".

Для преобразования текстовой информации в бинарный вид используются функции форматированного ввода: scanf, fscanf и sscanf. Исходная текстовая информация преобразуется в соответствии со строкой формата fmt, и полученные данные размещаются по переменным, адреса которых указаны в качестве аргументов args. Тип и количество аргументов определяются строкой формата. Функция scanf считывает исходную информацию со стандартного ввода, fscanf - из указанного файлового потока, а sscanf - из строкового буфера str.
function scanf(fmt:pchar; args:array of const):integer;
function fscanf(fp:pfile; fmt:pchar; args:array of const):integer;
function sscanf(str:pchar; fmt:pchar; args:array of const):integer;
int scanf(const char *fmt, ...);
int fscanf(FILE *fp, const char *fmt, ...);
int sscanf(const char *str, const char *fmt, ...);

Например, следующий оператор считывает очередное целое число из стандартного ввода:
var i:integer;
(* ... *)
scanf('%d',[@i]);
int i;
/* ... */
scanf("%d",&i);

Важно, что функции scanf передается адрес переменной i. Это необходимо для совместимости с языком Си, в котором параметры могут передаваться только по значению, а чтобы функция scanf изменяла переменную, которая находится в вызывающей процедуре, следует передать указатель, содержащий адрес этой переменной. Можно очень легко забыть про символ @, что приведет к ошибке записи в память. Нельзя также передавать неинициализированный указатель. Новичкам также приходится бороться с искушением помещать знак @ перед всеми указателями, такими как имена символьных массивов (pchar).

Строка формата fmt может содержать:

Функции форматированного ввода считывают указанные в строке формата пробельные и непробельные символы, но не сохраняют их. Специфкации формата сообщают функциям scanf о необходимости считать и преобразовать очередные символы из входного потока в значения определенного типа и сохранить эти значения по адресам, задаваемым соответствующими аргументам args. Тип значений определяется по полю TYPE спецификации формата:

Поле SIZE уточняет тип результата преобразования:

Следует отметить, что библиотека STDIO в FreePascal работает только с вещественными числами по стандарту IEEE-754, поэтому платформенно-зависимый тип REAL не поддерживается - необходимо использовать один из стандартных вещественных типов: SINGLE, DOUBLE, EXTENDED.

Поле WIDTH определяет максимальное количество символов входного потока, которое может быть использовано для преобразования по данной спецификации.

Если спецификация содержит знак '*', это означает, что данной спецификации не требуется никакой аргумент, а результат преобразования нигде не будет сохранен.

Спецификация %[шаблон] определяет набор символов. До тех пор, пока очередной символ входного потока есть среди перечисленных в этом наборе, преобразование входного потока происходит по данной спецификации. Набор может задаваться простым перечислением символов:

%[abcd]
либо с использованием диапазонов:
%[a-d]
либо с использованием отрицания:
%[^abc]
Последний шаблон соответствует всем символам, кроме 'a', 'b' и 'c'. Если требуется включить в шаблон символ '-', он должен стоять первым или последним символом шаблона. Если требуется включить в шаблон символ '^', он должен стоять последним символом шаблона.

Примеры спецификаций:
Спецификация Входной поток Аргументы и результат
%d %2d %f %s
11 12 34.07 keith ben
i:integer=11; j:integer=12; x:single=34.07; s1:array [0..N] of char=('k','e','i','t','h',#0)
123 456 4.7 qwerty
i:integer=123; j:integer=45; x:single=6; s1:array [0..N] of char=('4','.','7',#0)
%*d%1s%1c
123   456
 
s:array [0..N] of char=('4',#0); c:char='5';
%*d%1c%1s c:char=' '; s:array [0..N] of char=('4',#0);
%[A-Za-z]%n %s
My1stComp
s1:array [0..N] of char=('M','y',#0); n:integer=2; s2:array [0..N] of char=('1','s','t','C','o','m','p',#0)
mycomp the1st
s1:array [0..N] of char=('m','y','c','o','m','p',#0); n:integer=6; s2:array [0..N] of char=('t','h','e','1','s','t',#0)

Функции scanf возвращают количество сохраненных значений (количество обработанных спецификаций, кроме %*TYPE). Функция может завершить свою работу до того, как будет разобрана вся строка формата, если во входном потоке очередной непробельный символ будет конфликтовать с очередной спецификацией формата либо входной поток завершен (достигнут конец файла). Опытные программисты обычно не используют функцию fscanf (scanf) для ввода данных, кроме случаев простого интерактивного ввода. Вместо функции fscanf (scanf) предлагается использовать комбинацию вызовов fgets (gets) и sscanf. Недостаток функции fscanf (scanf) в данном случае состоит в том, что при случайном нарушении формата строки вводимых данных эта функция может перейти к чтению следующей строки, поскольку функция fscanf (scanf) не отличает символ окончания строки от других разделителей полей. В такой ситуации сложно обработать ошибку входных данных корректно, а кроме того, ввод следующей строки тоже будет нарушен.

Еще один комплексный пример:
program ex3;
uses StdIO, Strings;

Type
 rc_list=^rc_entry;
 rc_entry=record
  key:pchar;
  val:single;
  next:rc_list;
 end;

function parse_rc_file(filename:pchar):rc_list;
 var frc:pfile;
 buf,key:array [0..255] of char;
 const lrc:rc_list=nil; p:rc_list=nil;
begin
 frc:=fopen(filename,'r');
 if frc=nil then begin parse_rc_file:=nil; exit; end;
 while fgets(buf,255,frc)<>nil do begin
  p:=lrc;
  new(lrc);
  if lrc=nil then begin parse_rc_file:=nil; exit; end;
  lrc^.next:=p;
  if sscanf(buf,' %[A-Za-z0-9] = %g',[key,@lrc^.val])=2
  then begin
   lrc^.key:=stralloc(strlen(key)+1);
   strcopy(lrc^.key,key);
  end
  else begin (* ошибка формата - пропускаем строку *)
   fprintf(stderr,'Incorrect syntax: %s'#10,[buf]);
   dispose(lrc);
   lrc:=p;
  end;
 end;
 fclose(frc);
 parse_rc_file:=lrc;
end;

var
 rlc:rc_list; 
 filename:string;
begin
 if ParamCount<1 then begin
  fprintf(stderr,'Missing cmd-line params'#10,[]);
  halt(1);
 end;
 filename:=ParamStr(1);
 rlc:=parse_rc_file(@filename[1]);
 while rlc<>nil do begin
  printf('[%s]=%g'#10,[rlc^.key,rlc^.val]);
  rlc:=rlc^.next;
 end;
 
end.
#include <stdio.h>
#include <malloc.h>
#include <string.h>

struct rc_entry {
 char *key;
 float val;
 struct rc_entry *next;
};
typedef struct rc_entry *rc_list;

rc_list parse_rc_file(const char* filename)
{
 FILE *frc;
 char buf[255],key[255];
 rc_list lrc=NULL,p;
 frc=fopen(filename,"r");
 if (!frc) return NULL;
 while (fgets(buf,255,frc)) {
  p=lrc;
  lrc=malloc(sizeof(struct rc_entry));
  if (!lrc) return NULL;
  lrc->next=p;
  if (sscanf(buf," %[A-Za-z0-9] = %g",key,&lrc->val)==2)
  {
   lrc->key=malloc(strlen(key)+1);
   strcpy(lrc->key,key);
  }
  else { /* ошибка формата - пропускаем строку */
   fprintf(stderr,"Incorrect syntax: %s\n",buf);
   free(lrc);
   lrc=p;
  }
 }
 fclose(frc);
 return lrc;
}

int main(int argc, char **argv) 
{
 rc_list rlc;
 char *filename;
 if (argc<2) {
  fprintf(stderr,"Missing cmd-line params\n");
  return 1;
 }
 filename=argv[1];
 rlc=parse_rc_file(filename);
 while (rlc) {
  printf("[%s]=%g\n",rlc->key,rlc->val);
  rlc=rlc->next;
 }
 return 0;
}

Приведенная программа содержит функцию parse_rc_file, которая обрабатывает передаваемый ей текстовый файл, разбирая его по строкам. Такой текстовый файл может содержать настройки программы в виде:

параметр=значение
Функция parse_rc_file считает, что имена параметров задаются алфавитно-цифровыми идентификаторами, а их значения - вещественные числа. В результате функция возвращает список параметров (указатель на структуру rc_entry) или nil в случае ошибки. Функция корректно обрабатывает любые "лишние" пробелы благодаря использованию sscanf. В случае обнаружения в файле строки, не соответствующей описанному формату, она пропускается, а в стандартный файловый поток диагностических сообщений выдается предупреждение. Основная часть программы в данном примере лишь выводит содержимое получившегося списка на стандартный вывод.