Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Прикладное программирование на CC++ с нуля до мультимедийных и сетевых приложений

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
Функции 41
Здесь сначала описана главная функция, а затем функция STEP. По­скольку естественный порядок описания функций нарушен, мы должны ин­формировать функцию main о том, что где-то в программе описана функция STEP, которую следует использовать. Нужно объявить (декларировать) ее. Как видно из примера, делается это очень просто. Функция STEP имеет тип double, и ее объявляют в main вместе с описаниями переменных этого типа. Для того чтобы система могла отличить объявление переменной от объявле­ния функции, после имени функции в описании ставят круглые скобки.
Функция STEP от двух аргументов: õ (вещественного типа) и n (целого типа), как легко убедиться, возвращает n-ю степень числа õ для неотрица­тельных целых n. Главное, на чем следует остановиться, это то, что в теле функции STEP нам пришлось описать две дополнительные переменные — i è s, после чего изготовленная нами функция стала совсем похожей на уже со­всем родную нам главную функцию main. Мы упоминали о том, что пере­менные, описанные внутри функции, являются локальными. Они «живут» только в этой функции и для других функций не существуют. В этой связи имена локальных переменных в разных функциях вполне могут совпадать. Переменная õ1 в функции Ì не имеет никакого отношения к переменной õ1 в функции Ð так же, как Миша, живущий в квартире 56, это вовсе не то же самое, что Миша, живущий в квартире 65. Совсем иное дело — глобальные переменные, описанные в программе вне функций. Такие переменные (и константы) «живут» во всех функциях, описанных после глобальных пере­менных. Такие объекты видны из любой функции программы. Изменение значения глобальной переменной, сделанное в одной из функций програм­мы, немедленно отобразится на ее значении во всех остальных функциях. Продолжая нашу аналогию, можно сказать, что Александр Сергеевич Пуш­кин — он и есть Пушкин и для квартиры 56, и для квартиры 65, и если пре­зидентом на выборах стал господин Буш, то он стал президентом и для штата Техас, и для штата Флорида.
Правда, и здесь возможны особенности. А что, если в некой семье с не­стандартным воображением родившегося ребенка назвали А. С. Пушкин? Ко­гда в этой семье говорят о Пушкине, то кого имеют в виду? В житейской си­туации, по-видимому, в этом случае требуются дополнительные пояснения. В С ничего такого не нужно. Существует следующее правило: если имя гло­бальной переменной совпадает с именем локальной для некоторой функции переменной, то внутри этой функции операции с этим именем относятся к локальному объекту. Так, например, изменение локальной переменной никак не отразится на значении глобальной переменной с тем же именем. Наоборот, изменение глобальной переменной проявится во всех функциях, где такое имя не описано, а там, где описана ее локальная «тезка», с последней ничего не произойдет.
Мы говорили пока о типизированных функциях, вычисляющих одно зна­чение и возвращающих это значение в качестве значения функции. Возмо­жен, однако, и другой вариант использования функций, когда требуется опре­делить и передать в программу несколько значений. В некоторых языках про граммирования такие программные модули носят название процедур.
-
42 Глава 1. Язык программирования С
Типичная для этого варианта задача — преобразование координат. Допустим, нам нужно создать функцию, которая принимает в качестве аргументов по­лярные координаты точки ρ è ϕ и передает в вызывающую эту функцию про­грамму декартовы координаты точки Õ è Ó, вычисляемые по формулам
X =ρ ϕcos( ),Y =ρ ϕsin( )
.
Один из способов решения такой задачи состоит в передаче вычисленных значений в вызывающую программу в виде глобальных переменных:
#include<stdio.h> #include<math.h> #define Pi 3.1415927 double X,Y; void decart(double r,double f) { double pil80=Pi/180; X=r*cos(f); Y=r*sin(f); } void main() {double rr.ff; scanf("%lf %If'",&rr.&ff); decart(rr,ff); printf("%lf %lf",X,Y); }
Прежде всего, поясним новый элемент программы, появившийся в треть­ей строке. Мы уже говорили, что конструкции, начинающиеся символом #, являются инструкциями препроцессору. Такие инструкции предписывают системе еще до начала компиляции произвести над программой некоторые предварительные действия. Так, инструкция #include предписывает подклю­чить к программе соответствующий заголовочный файл. По инструкции #define ÀAÀ ÂÂÂ препроцессор просматривает весь текст программы и везде, где встречается фрагмент ÀÀÀ, производится его замена на фрагмент ÂÂÂ. Подчеркнем, что замена делается именно в тексте еще до компиляции про­граммы. Необходимость в использовании этой препроцессорной инструкции возникает только в особых случаях, с которыми, однако, читателю, возможно, придется столкнуться, что и побудило нас включить эту конструкцию в про­грамму.
Функция decart в данной программе не возвращает никакого значения и поэтому имеет тип void. В теле функции описана локальная переменная pil80, которая получает значение, используемое для преобразования углов из гра­дусной меры в радианную. Использование здесь локальной переменной явно нецелесообразно. Автор сознательно допустил это для того, чтобы читатель сам попробовал сделать необходимые корректировки. Передача в главную функцию вычисленных в decart значений осуществляется с помощью глобаль­ных переменных Õ è Y.
Передача результатов вычислений из одной функции в другую через гло­бальные переменные является не единственным и иногда и не лучшим спосо­бом. Альтернативой может быть передача значений по адресам, с использова­нием указателей. Начнем с того, что напомним — функция, приняв значения своих аргументов, работает не непосредственно с этими аргументами,асих
Функции 43
копиями. Так, если вы попытаетесь в теле функции изменить значение аргу­мента, то, выйдя из функции, обнаружите, что эти изменения в вызывающей функцию программе никак не отразились. Однако эту особенность можно обойти, если аргументы, которые вы хотите изменить, задать через их адреса. Теперь, если с ними обращаться по правилам работы с указателями, то можно передавать значения из функции в функцию по адресам, без использования глобальных переменных.
Продемонстрируем эти возможности на той же задаче, которую реализова­ла предыдущая
const double pil80=3.1415927/180; void DECART(double r,double f,double *x,double*y) { *x=r*sin(f*pil80); *y=r*cos(f*pil80); } void main() {double r,f,x,y; scanf("%lf %lf",&r,&f); DECART(r,f,&x,&y); printf("%lf %1f",x,y); }
программа. Сейчас она будет выглядеть следующим образом:
Для начала мы оптимизировали определение значения pi180. Теперь оно стало вещественной константой, которая не может и не должна меняться ни­где в программе. Кроме того, сразу задано ее числовое значение через деление пифагорова числа на 180. Эта операция делается в программе один раз, что и является наиболее оптимальным.
Функция DECART имеет четыре аргумента. Два первых — обычные пере­менные типа double, а третий и четвертый — указатели на double. Тело функ­ции состоит из двух выражений присваивания. Первое из них следует пони­мать так: «По адресу, указанному третьим аргументом функции, записать зна­чение, равное произведению первого аргумента на синус второго аргумента, умноженного на значение константы pi180». Аналогичную сентенцию можно сочинить и для второго выражения присваивания.
В главной функции описаны четыре вещественные переменные: r, f, õ, ó. Значения первых двух вводятся с консоли и далее используются как «вход­ные» параметры в функции DECART. Значения õ è ó формируются в резуль­тате работы этой функции. Поскольку на месте двух последних аргументов должны стоять указатели, мы написали в вызове функции DECART &x è &ó.
Изложенные только что представления позволят нам окончательно разо­браться с функцией scanf. Вспомним, что инициализируемую с консоли пере­менную мы снабжали символом & перед ее именем. Сейчас стало понятным, почему это необходимо. Функция scanf должна изменить значение вводимой переменной — единственная возможность сделать это без привлечения гло­бальных объектов заключается в использовании указателей. Именно по этой причине аргументом функции scanf является не сама переменная, а ее адрес.
Продолжим изучение функций уже на достаточно сложном примере с многократным последовательным вызовом нескольких функций. Заметим, что читатель, «не обремененный» некоторыми знаниями из высшей математики, будет вынужден пропустить следующий пример.
44 Глава 1. Язык программирования С
Рассмотрим вполне реальную задачу приближенного вычисления опреде­ленного интеграла по методу трапеций. Формула трапеций может быть пред­ставлена в следующей форме:
Здесь
h
ba
=
b
f x dx h
( ) ...
∫
a
−
,
y fx
= (),x a ih
ii
n
⎛
=
⎜ ⎝
yy
+
0
n
++++
2
=+
i
yy y
12
⎞ ⎟
n
⎠
.
.
Суть метода трапеции состоит в том, что интервал интегрирования разби­вается на n малых равных между собой отрезков длиной h. На каждом из этих отрезков подынтегральная площадь заменяется трапецией, площадь которой вычисляется по известной формуле. Точность вычисления интеграла зависит от числа разбиений n. Можно, увеличивая n, достичь желаемой относитель­ной точности. Мы в нашей программе будем поступать следующим образом. Вычислим интеграл при n разбиениях — получим значение J снова вычислим интеграл — получим значение J
JJ
−
nn
êà
2
, ãäå ε — заданная точность, то процесс удваивания n продол-
>
J
2
ε
n
. Если относительная ошиб-
2n
. Удвоим n è
n
жается. Рано или поздно (если интеграл «хороший») желаемая точность будет достигнута. А теперь составим программу, которая вычисляет конкретный ин-
теграл, например
b
3
x
:
dx
∫
3
a
double a.b; double FUNC(double x) { return x*x*x/3.; } double trap(double a,double b,int n) { int i=l; double h=(b-a)/n,t=0.5*(FUNC(a)+FUNC(b)); for(;i<n;) t+= return t*h; } double integral(double eps) { int n=4,z; double a0=trap(a,b,4),al; do
{al=trap(a,b,n+=n);
z=fabs (al-a0) >fabs (a1) *eps;
a0=al; } while(z); return al; } void main() { double epsilon; printf("a="); scanf("%lf",&a); printf("b="); scanf("%lf'",&b); printf("eps="); scanf("%lf",&epsilon); printf("INTEGRAL=%lf",integral(epsilon)); }
;
PUNC(a+h*i++);
Описаны две глобальные переменные à è b, которым будут присвоены значения пределов интегрирования. Функция FUNC предназначена для вы­числения значений подынтегрального выражения. Не составляет труда изме
-
Функции 45
нить конкретную функцию x3/З на любую другую. Функция trap реализует формулу трапеций для разбиения интервала интегрирования от à äî b на n от­резков. Три эти величины являются аргументами функции. Функция integral выполняет вычисление интеграла по методу трапеций с заданной точностью. Параметр eps определяет эту точность. Рекомендуется внимательно рассмот­реть тело функции и убедиться в том, что здесь действительно реализован ал­горитм достижения заданной точности.
Все три функции возвращают вещественные значения и используются в главной функции. В последней с консоли вводятся пределы интегрирования и точность. Функция печати вызывает функцию integral, которая вызывает функцию trap, которая, в свою очередь, обращается к функции FUNC. Такие многократные, вложенные друг в друга вызовы функций являются типичны­ми структурами алгоритмических языков программирования.
Рассмотрим еще один пример на использование «самодельной» функции без параметров. В этом примере мы познакомимся с еще одним генератором случайных чисел, описанным в заголовочном файле stdlib.h. Эта функция на­зывается random, имеет аргумент типа int и возвращает целое случайное число из диапазона от 0 до N – 1, ãäå N — значение аргумента функции. С помощью этой функции изготовим генератор вещественных случайных чисел в диапазо­не от 0 до 1. В главной функции генератор будет использован для заполнения вещественного массива случайными числами:
#include<stdlib.h> const double g=l./31999.; double RN() { return random(32000)*g; } void main() { int i=0; double a[10]; while(i<10) printf("%lf\n" ,a[i++]=RN()); }
Легко видеть, что функция RN, которой не нужны аргументы, с использо­ванием заготовленной глобальной константы g реализует генерацию случай­ной величины в нужном диапазоне. Обратим внимание на то, что в главной программе производится печать очередного элемента массива, который прямо в функции печати и получает значение.
Следующая функция заполняет n первых элементов массива случайными целыми в диапазоне от 0 до 9:
void initarray(int *name. int n) {do *(name+- -n)=random(10); while(n); }
Эта функция может быть использована, например, в следующей главной функции:
void main() { int mass[100],k; printf("ЗАПОЛНИТЬ K="); scanf("%d",&k); initarray(mass,k); printf("ЭЛЕМЕНТОВ МАССИВА\п"); while(k) printf("%d\n",mass[- -k]);}
46 Глава 1. Язык программирования С
Последний вопрос, который мы рассмотрим в отношении функций, свя­зан с использованием имен функций в качестве аргументов функций. Сразу обратимся к примеру программы:
double function1(double x) { return (x+3.2)*(x-4.7); } double function2(double x) { return (x*x-3.*x-.2)*7.4; }
double minimum(double a,double b.int n,double func(double)) {double h=(b-a)/n,g=1e32,min=func(a); int i=l; while(i<=n) if(g=func(a+h*i++))<min) min=g; return min; } void main() { printf("MINIMUM function1=%e\n MINIMUM functioii2=%e", minimum(-10.0,+10.0,1000,function1), minimum(-10.0,+10.0,1000,function2)); }
Две первые функции в программе вычисляют значения некоторых алгеб­раических выражений — полиномы второй степени от x. Функция minimum воз­вращает значение, полученное с некоторой точностью, доставляющее мини­мум функции с именем func. Интервал [àb], на котором находится минимум, разбивается на n отрезков и на конце каждого отрезка с координатой à+h*i проверяется, имеет ли функция значение, меньшее по сравнению с предыду­щим значением. Разобраться более детально с поиском минимума читателю предлагается самостоятельно. Нас сейчас интересует оформление функции minimum. Она имеет четыре параметра, три из которых — обычные аргументы: à è b — концы интервала, n — число разбиений интервала. Четвертый параметр является именем функции, с которой предстоит работать функции minimum. Пока конкретный вид функции func не задан — func является формальным име­нем, а фактическое имя будет задаваться только в главной функции. Для того чтобы компилятор «понял», что последний аргумент есть имя функции, после имени стоят скобки, а для того чтобы компилятор «знал», что формальная функция имеет один аргумент типа double, в скобках стоит название типа. Об­ратите внимание, что после названия типа нет имени аргумента. Кроме того, необходимо указать и тип формальной функции. В нашем случае это double. Те­перь обратимся к главной функции. Она дважды вызывает функцию minimum. Первый раз в качестве фактического имени использовано имя function1, à âòî­ðîé ðàç — function2. При этом указывается только имя функции без скобок и аргументов. Таким образом, используя в качестве аргументов формальные име­на функций, можно создавать универсальные функции, обеспечивающие вызо­вы различных конкретных функций.
Файлы
До сих пор возможности общения наших программ с окружающим миром были весьма ограничены. Мы могли только вводить данные в программу с консоли и выводить их на экран. Теперь наша задача состоит в том, чтобы
Файлы 47
научиться записывать результаты работы программы на диск и считывать дан­ные с диска в программу.
Как известно, любые данные хранятся на магнитных носителях информа­ции (магнитных дисках) в виде файлов. Представляя файл как «папку» с ин­формацией, что дословно и соответствует английскому слову file, мы должны освоить процедуры открытия и закрытия этих «папок» и процедуры чтения и записи файлов.
Сразу начнем со следующей программы:
void main() { int i; FILE *f; f=fopen("one.dat","w"); for(i=l; i<15; i++) fprintf(f,"%d\n",i); fclose(f); }
Здесь мы впервые столкнулись с еще одним типом данных. Название это­го типа FILE (пишется именно заглавными буквами, что для С нехарактерно). Переменная f имеет отношение к этому новому типу. Поскольку она фигури­рует в описании со стоящей перед ней звездочкой, многоопытный читатель сделает вывод о том, что f является указателем. Так оно и есть, и указывает этот указатель на файл. Иными словами, f является адресом начала файла. Пока, однако, этот указатель не инициализирован, не привязан ни к какому конкретному файлу. Инициализация указателя на файл (мы будем использо­вать также термин «файловая переменная») производится одновременно с от­крытием файла, предоставлением файла в распоряжение данной программе. Открытие файла производится функцией fopen и выглядит как выражение присваивания:
ИмяФайловойПеременной=fореn("Строка1", "Строка2");
Эта процедура действительно является присваиванием, поскольку функ­ция fopen, возвращающая адрес файла, который она открывает, имеет тип FILE*. Функция имеет два аргумента. Строка1 задает имя открываемого фай­ла. В DOS полное имя файла состоит, как правило, из собственно имени и расширения, разделенных точкой. В нашем примере открывается файл one.dat. Строка2 состоит из буквы, показывающей, для каких операций от­крывается файл. Возможны следующие варианты:
•
«r» — файл открывается для чтения из него информации (read);
•
«w» — файл открывается для записи информации (write);
•
«a» — файл открывается для дозаписи информации в конец файла (append).
Буква w предполагает создание нового файла с указанным в Строке1 име­нем. Будьте внимательны: если на диске в рабочем каталоге уже существовал файл с таким именем, то его открытие для записи приведет к безвозвратной (и, вероятно, безвременной) утрате старого файла.
Данная программа создает новый файл и записывает в него 14 целых чи сел от 1 до 14. Каждое число записывается в новую строку. Функция записи в
-
48 Глава 1. Язык программирования С
ôàéë fprintf весьма похожа на знакомую вам функцию печати на экран printf. Разница состоит в том, что сразу за открывающей скобкой пишется файловая переменная, а далее после запятой все полностью совпадает с правилами оформления печати на экран. Информация, записанная в файл, выглядит точно так, как и на экране. В этом можно убедиться, просмотрев созданный файл с помощью любого текстового редактора, в частности, и из системы TURBO Ñ. Поработав с папкой, вы, наверное, ее закрываете. Это же следует сделать и с файлом. Функция закрытия файла fclose имеет в качестве аргумен­та имя файловой переменной. Можно отметить, что в случае успешного, как говорят военные — штатного, завершения программы все открытые файлы закрываются автоматически. Тем не менее, мы настоятельно рекомендуем вы­зывать функцию fclose сразу после того, как программа завершила работу с соответствующим файлом.
Теперь напишем программу, которая читает данные из файла. Считаем, что на диске существует файл one.dat, созданный предыдущей программой:
void main() { int i,j=10.k; FILE *pp; Pp=fopen("one.dat","r"); for(i=0; i<j; i++) { fscanf(pp,"%d",&k); printf("%d\n",k); } fclose(pp); }
В программе файл one.dat открывается для чтения. В цикле с помощью функции чтения из файла fscanf в переменную k из этого файла читается оче­редное число, и его значение выводится на экран. После чтения десяти чисел файл закрывается, и программа завершает работу.
Функция fscanf также похожа на функцию ввода с клавиатуры scanf. В ней на первом месте стоит указатель на файл. С оформлением форматной строки дело обстоит несколько сложнее. Прежде всего, нужно знать, что если в фор­матной строке стоит некий символ, то вы должны быть уверены, что текущая позиция читаемого файла содержит именно этот символ. Например, если в форматной строке стоит \n, то и в файле в этот момент находится символ пе­рехода на новую строку. В этой связи рекомендуется, по крайней мере, на первых порах, вообще не писать в форматной строке ничего, кроме формат­ных выражений. Избегайте даже пробелов. Учитывая эти тонкости, разработ­чики системы позаботились о том, чтобы функция fscanf в какой-то степени сама разбиралась с размещением записей в файле. Как бы ни размещались числа: каждое в своей строке, группами в строках, все в одной строке — вы просто пишете подряд столько форматных выражений, сколько нужно прочи­тать чисел (или символов). Не обращайте внимание на то, как расположены данные в файле. Можете считать, что они все записаны подряд в строчку.
Нормально сформированный файл всегда имеет последним символом признак конца файла EOF (end of file). Существует специальная функция feof, имеющая аргументом файловую переменную, которая показывает, достигнут
Файлы 49
ли конец файла. Для более ясного представления о работе функции feof пред­ставим файл в виде киноленты, которая прокручивается так, что после оче­редного чтения данных следующий кадр устанавливается в «файловое окно». В качестве кадра надо понимать очередной байт, записанный в файл. В самом последнем кадре файла и находится признак конца файла EOF. Если после очередного чтения из файла в «файловом окне» оказался признак конца, то при вызове функции feof она возвращает 1, иначе конец файла не достигнут, и функция возвращает 0.
Рассмотрим новую стандартную функцию int getc(FILE *f). Как видно из такого ее формального определения, функция getc возвращает целое число и имеет своим аргументом указатель на файл. Функция читает из файла очеред­ной символ (байт) и возвращает его код (значение). Обратим внимание на то, что читается один байт, а возвращаемое значение конвертируется в тип int, занимающий два байта.
Функция int putc(int ñ,FILE *f) записывает в очередную позицию файла символ, код которого задан первым аргументом. Первый аргумент имеет тип int, a при записи в файл значение конвертируется в тип char. Таким образом, на месте первого параметра может находиться как код символа, так и сам символ. Функция возвращает код записанного символа, что может быть ис­пользовано в программе.
Теперь уместно, безотносительно к текущей теме — файлам, рассмотреть еще несколько полезных функций. Функция int getch(void), нам уже знакомая, читает символ, скажем так, из стандартного файла ввода/вывода. А стандарт­ным файлом назовем консоль. Следовательно, функция вводит символ с кла­виатуры. До сих пор мы пользовались этой функцией для того, чтобы сохра­нить открытым рабочий экран до нажатия любой клавиши. Это действитель­но важное приложение функции, но ее основным назначением является передача в программу символа, считываемого с клавиатуры.
Функция int putch(int ñ), наоборот, передает символ, заданный своим ар­гументом, в стандартный файл ввода/вывода. А чтобы перестать испытывать терпение читателя, то автор будет вынужден сказать по-простому: функция печатает на экран символ — свой аргумент.
В некоторых реализациях систем программирования, в частности, в
TURBO С, вы можете встретить модификации этих функций getchar, putchar, getche и putche. Эти разновидности описанных выше функций отличаются ре-
жимом работы, в том числе буферизацией или не буферизацией ввода. Сказав эти загадочные слова, автор этим и ограничится, отослав любопытного чита­теля к справочной системе TURBO C++ и к литературе.
Наряду с чтением и записью одиночных символов существуют функции чтения и записи строк, как для консоли, так и для файлов. Эти функции име­ют имена gets, puts, fgets è fputs.
Функция char* gets(char* s) читает строку символов с клавиатуры (до на­жатия клавиши ввода) и помещает ее в память, начиная с адреса s. При этом в качестве аргумента функции целесообразно использовать имя описанного в программе символьного массива, в который будет помещена прочитанная с консоли строка.
50 Глава 1. Язык программирования С
Функция int puts(const char* s) может иметь своим аргументом указатель на символ — имя символьного массива или непосредственно строку символов в кавычках. Строка или массив выводится на экран.
Из файла может быть прочитана или в файл может быть записана строка. Первую из этих задач выполняет функция char* fgets(char*, int, PILE* ). Она читает из файла, на который указывает последний аргумент, очередную стро­ку, записывает ее в память по адресу, на который указывает первый аргумент. При этом читается количество символов, не превышающее значения второго аргумента, уменьшенного на единицу. В конец записанной в память строки автоматически добавляется признак конца строки \0. Сама функция, в соот­ветствии с ее типом char*, возвращает адрес начала строки.
Следующая программа, текст которой будем считать находящимся в фай­ле re.ñ, построчно читает сама себя и выводит на экран длины строк:
#include<stdio.h> #include<string.h> void main() { FILE *f; char *p; int length; f=fopen("re.c","r"); while(!feof(f)) {fgets(p,256,f); length=strlen(p); printf("%d\n",length); }}
Ограничитель длины читаемой строк здесь выбран равным 256, поскольку в редакторе TURBO Ñ недопустимы строки длиннее 255 знаков. Можно обой­тись без переменной length и использовать в цикле while единственное выра­жение printf(«%d\n»,strlen(fgets(p,256,f))). Обратите внимание на то, что мы не пытались описать в программе символьный массив, в который записывается очередная строка. Вместо этого мы создали только указатель ð íà char — адрес начала строки. Так поступать вполне допустимо, хотя душа автора этому не­произвольно противится, поскольку при этом конкретная область памяти под строку, казалось бы, не выделяется. В реальной программе автор описал бы массив ð[256] и после этого спал бы по ночам спокойным сном, чего и чита­телю желает.
Функция int fputs (char*, FILE*) записывает в файл, указанный последним аргументом, строку, расположенную по адресу, указанному первым аргумен­том. Возвращается количество записанных символов.
Если выполняются строковые операции со стандартным файлом вво­да/вывода, то есть с консолью, то используются функции char* gets (char*) è int puts(const char*). Наверное, уже нет нужды пояснять, что является аргу­ментами этих функций и какие значения эти функции возвращают.
Широкие возможности программисту предоставляет так называемый пря­мой доступ к файлу. Используя нашу аналогию файла с кинолентой, можно сказать, что прямой доступ к файлу позволяет устанавливать файловое окно на любой заданный кадр. Установка очередной позиции осуществляется функци
-