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

книги / Программирование на языке Си

..pdf
Скачиваний:
15
Добавлен:
12.11.2023
Размер:
17.16 Mб
Скачать

322

Программирование на языке Си

в одном байте формируется последовательность битов, приве­ денная на рис. 6.5.

 

7

6

5

4

3

2

1

0

Номера битов:

0

1

0

0

0

1

0

0

Битовые поля:

 

hh.y

 

 

 

hh.x

 

 

Рис. 6.5. Размещение битовых полей в байте

В качестве иллюстрации возможностей объединений и структур с битовыми полями рассмотрим следующую програм­ му. В ней вводятся значения двух целых переменных т и п и остатки от их деления на 16 заносятся соответственно в четыре младших и четыре старших разряда одного байта. Таким обра­ зом выполняется некоторая кодировка введенных значений пе­ ременных т и п . Затем печатается изображение содержимого сформированного байта. Особенности программы объяснены в комментариях и поясняются результатами выполнения. Обрати­ те внимание на использование объединений. В функции cod() запись данных производится в элементы (битовые поля) струк­ туры hh, входящей в объединение un, а результат выбирается из того же объединения un в виде одного байта. В функции Ыпаг() происходит обратное преобразование - в нее как значение па­ раметра передается байт, содержимое которого побитово "расшифровывается" за счет обращения к отдельным полям структуры byte, входящей в объединение cod.

Текст программы:

/ * С т р у к т у р ы , о б ъ е д и н е н и я и б и т о в ы е п о л я * /

# i n c l u d e < s t d i o . h > v o i d m a in ( v o i d )

{

u n s i g n e d c h a r k ; i n t m , n ;

Глава 6. Структуры и объединения

323

void binar(unsigned char); /* Прототип функции */

unsigned char cod(int,int); /* Прототип функции */

printf("\n m="); scanf("%d",6m); printf(" n="); scanf("%d",6n); k=cod(m,n); printf(" cod=%u",k); binar(k);

)

/* Упаковка в один байт остатков от деления на 16 двух целых чисел */

unsigned char cod(int a, int b)

{

union

{

unsigned char z ; struct

{

unsigned int x :4; /* Младшие биты */ unsigned int у :4; /* Старшие биты */

}hh;

}un ;

un .hh.x=a%16; un.hh.y=b%16; return un.z ;

}

/* binar - двоичное представление байта */ void binar(unsigned char ch)

{

union

{

unsigned char ss; struct

{

unsigned aO :1; unsigned al :1 unsigned a2 :1; unsigned аЗ :1 unsigned a4 :1; unsigned a5 :1 unsigned a6 :1; unsigned a7 :1

} byte; ) cod ;

21

324

Программирование на языке Си

cod.ss=ch;

Номера

битов:

 

"

 

 

 

printf("\n

3

1

0") ;

"7

6

5

 

4

2

print£("\n Значения

битов:

%d

 

%d

"

"%d

 

%d

" %d

%d

%d

 

%d",

 

cod.byte.a7,

cod.byte.a6,

 

cod.byte.a5,

cod.byte.a4,

cod.byte.a3,

 

cod.byte.a2,

cod.byte.al,

cod.byte.aO);

 

}

Результаты выполнения программы на IBM PC: Первый вариант:

m=l

 

 

 

 

 

 

 

 

n=3

 

 

 

 

 

 

 

 

cod=4 9

7

6

5

 

4 3

2

1

0

Номера битов:

1

Значения битов:

0

0

1

0

0

0

1

Второй вариант:

т=0

п=1

cod=16

Номера битов:

7

6

5

4

3

2

1

0

Значения битов:

0

0

0

1

0

0

0

0

Глава 7

ВВОД и вывод

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

Особенностью языка Си, который впервые был применен при разработке операционной системы UNIX, является отсутст­ вие заранее спланированных структур файлов. Все файлы рас­ сматриваются как неструктурированная последовательность байтов. При таком подходе к организации файлов удалось рас­ пространить понятие файла и на различные устройства. В UNIX конкретному устройству соответствует так называемый "спе­ циальный файл", а одни и те же функции библиотеки языка Си используются как для обмена данными с файлами, так и для об­ мена с устройствами.

Примечание. В UNIX эти же функции используются также для обмена данными между процессами (выполняющимися программами).

Библиотека языка Си поддерживает три уровня вводавывода: потоковый ввод-вывод, ввод-вывод нижнего уровня и ввод-вывод для консоли и портов. Последний уровень, обеспе­ чивающий удобный специализированный обмен данными с дис­ плеем и портами ввода-вывода, мы рассматривать не будем в силу его системной зависимости. Например, он различен для MS-DOS, Windows и UNIX.

7.1. П отоковы й ввод-вы вод

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

326

Программирование на языке Си

тывание или запись фиксированной порции данных. Чаще всего минимальной порцией данных, участвующей в обмене с внеш­ ней памятью, являются блоки в 512 байт или 1024 байта. При вводе с диска (при чтении из файла) данные помещаются в бу­ фер операционной системы, а затем побайтно или определен­ ными порциями передаются программе пользователя. При выводе данных в файл они накапливаются в буфере, а при за­ полнении буфера записываются в виде единого блока на диск за одно обращение к последнему. Буферы операционной систе­ мы реализуются в виде участков основной памяти. Поэтому пе­ ресылки между буферами ввода-вывода и выполняемой прог­ раммой происходят достаточно быстро в отличие от реальных обменов с физическими устройствами.

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

При работе с потоком можно производить следующие дейст­ вия:

открывать и закрывать потоки (связывать указатели на по­ токи с конкретными файлами);

вводить и выводить: символ, строку, форматированные данные, порцию данных произвольной длины;

анализировать ошибки потокового ввода-вывода и условие достижения конца потока (конца файла);

управлять буферизацией потока и размером буфера;

получать и устанавливать указатель (индикатор) текущей позиции в потоке.

Для того чтобы можно было использовать функции библио­ теки ввода-вывода языка Си, в программу необходимо включить заголовочный файл stdio.h (#include <stdio.h>), который содер­ жит прототипы функций ввода-вывода, а также определения констант, типов и структур, необходимых для работы функций обмена с потоком.

Глава 7. Ввод и вывод

327

На рис. 7.1 показаны возможные информационные обмены исполняемой программы на локальной (несетевой) ЭВМ.

Рис. 7.1. Информационные обмены исполняемой программы на локальной ЭВМ

7.1.1. Открытие и закрытие потока

Прежде чем начать работать с потоком, его необходимо ини­ циализировать, т.е. открыть. При этом поток связывается в ис­ полняемой программе со структурой предопределенного типа FILE. Определение структурного типа FILE находится в заго­ ловочном файле stdio.h. В структуре FILE содержатся компо­ ненты, с помощью которых ведется работа с потоком, в частности: указатель на буфер, указатель (индикатор) текущей позиции в потоке и другая информация.

При открытии потока в программу возвращается указатель на поток, являющийся указателем на объект структурного типа FILE. Этот указатель идентифицирует поток во всех после­ дующих операциях.

328

Программирование на языке Си

Указатель на поток, например fp, должен быть объявлен в программе следующим образом:

#include <stdio.h> FILE *fp;

Указатель на поток приобретает значение в результате вы­ полнения функции открытия потока:

fp = fopen {имя файла, режим открытия)',

Параметры имяфайла и режимоткрытия являются указа­ телями на массивы символов, содержащих соответственно имя файла, связанного с потоком, и строку режимов открытия. Од­ нако эти параметры могут задаваться и непосредственно в виде строк при вызове функции открытия файла:

fp = fopen("t.txt", "г");

где t .txt - имя некоторого файла, связанного с потоком;

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

Стандартно файл, связанный с потоком, можно открыть в одном из следующих шести режимов:

"w" - новый текстовый (см. ниже) файл открывается для записи. Если файл уже существовал, то предыдущее содержимое стирается, файл создается заново;

"г" - существующий текстовый файл открывается только для чтения;

"а" -текстовый файл открывается (или создается, если файла нет) для добавления в него новой порции ин­ формации (добавление в конец файла). В отличие от режима "w" режим "а" позволяет открывать уже существующий файл, не уничтожая его предыдущей версии, и писать в продолжение файла;

"w+" - новый текстовый файл открывается для записи и последующих многократных исправлений. Если файл уже существует, то предыдущее содержимое стирается. Последующие после открытия файла за­ пись и чтение из него допустимы в любом месте

Глава 7. Ввод и вывод

329

файла, в том числе запись разрешена и в конец файда, т.е. файл может увеличиваться ("расти");

"Н-" - существующий текстовый файл открывается как для чтения, так и для записи в любом месте файла; од­ нако в этом режиме невозможна запись в конец файла, т.е. недопустимо увеличение размеров фай­ ла;

"а+" -текстовый файл открывается или создается (если файла нет) и становится доступным для изменений, т.е. для записи и для чтения в любом месте; при этом в отличие от режима "w+" можно открыть су­ ществующий файл и не уничтожать его содержи­ мого; в отличие от режима "г+" в режиме "а+" можно вести запись в конец файла, т.е. увеличивать его размеры.

Поток можно открыть в текстовом либо двоичном (би­ нарном) режиме.

В текстовом режиме прочитанная из потока комбинация символов CR (значение 13) и LF (значение 10), то есть управ­ ляющие коды "возврат каретки" и !'перевод строки", преобразу­ ется в один символ новой строки V (значение 10, совпадающее с LF). При записи в поток в текстовом режиме осуществляется обратное преобразование, т.е. символ новой строки '\п' (LF) за­ меняется последовательностью CR и LF.

Если файл, связанный с потоком, хранит не текстовую, а произвольную двоичную информацию, то указанные преобразо­ вания не нужны и могут быть даже вредными. Обмен без такого преобразования выполняется при выборе двоичного или бинар­ ного режима, который обозначается буквой Ь. Например, "г+Ь" или "wb". В некоторых компиляторах текстовый режим об­ мена обозначается буквой t, т.е. записывают "a+t" или "rt".

Если поток открыт для изменений, т.е. в параметре режима присутствует символ "+", то разрешены как вывод в поток, так и чтение из него. Однако смена режима (переход от записи к чтению и обратно) должна происходить только после установки указателя потока в нужную позицию (см. §7.1.3).

330

Программирование на языке Си

При открытии потока могут возникнуть следующие ошибки: указанный файл, связанный с потоком, не найден (для режима "чтение"); диск заполнен или диск защищен от записи и т.п. Не­ обходимо также отметить, что при выполнении функции fopen() происходит выделение динамической памяти. При ее отсутствии устанавливается признак ошибки "Not enough memory" (недостаточно памяти). В перечисленных случаях ука­ затель на поток приобретает значение NULL. Заметим, что ука­ затель на поток в любом режиме, отличном от аварийного, никогда не бывает равным NULL.

Приведем типичную последовательность операторов, кото­ рая используется при открытии файла, связанного с потоком:

if ((fp = fopen("t.txt", "w")) == NULL)

{

perror("ошибка при открытии файла t.txt \n"); exit(0);

}

где NULL - нулевой указатель, определенный в файле stdio.h.

Для вывода на экран дисплея сообщения об ошибке при от­ крытии потока используется стандартная библиотечная функция реггог(), прототип которой в stdio.h имеет вид:

void perror (const char * s);

Функция perror() выводит строку символов, адресуемую указателем s, за которой размещаются: двоеточие, пробел и со­ общение об ошибке. Содержимое и формат сообщения опре­ деляются реализацией системы программирования. Текст сооб­ щения об ошибке выбирается функцией реггог() на осно­ вании номера ошибки. Номер ошибки заносится в переменную int errno (определенную в заголовочном файле errno.h) рядом функций библиотеки языка Си, в том числе и функциями вводавывода.

После того как файл открыт, с ним можно работать, записы­ вая в него информацию или считывая ее (в зависимости от ре­ жима).

Глава 7. Ввод и вывод

331

Открытые на диске файлы после окончания работы с ними рекомендуется закрыть явно. Для этого используется библио­ течная функция

int fclose ( указатель на поток );

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

7.1.2. С тандартны е ф айлы и ф ункции для работы с ними

Когда программа начинает выполняться, автоматически от­ крываются пять потоков, из которых основными являются:

стандартный поток ввода (на него ссылаются, используя предопределенный указатель на поток stdin);

стандартный поток вывода (stdout);

стандартный поток вывода сообщений об ошибках

(stderr).

По умолчанию стандартному потоку ввода stdin ставится в соответствие клавиатура, а потокам stdout и stderr соответству­ ет экран дисплея.

Для ввода-вывода данных с помощью стандартных потоков в библиотеке языка Си определены следующие функции:

getchar( )/putchar() - ввод-вывод отдельного символа;

gets( )/puts() - ввод-вывод строки;

scanf( )/printf() - ввод-вывод в режиме форматирования данных.

Ввод-вывод отдельных символов. Одним из наиболее эф­ фективных способов осуществления ввода-вывода одного сим­ вола является использование библиотечных функций getchar() и putchar( ). Прототипы функций getchar( ) и putchar() имеют следующий вид:

int getchar(void); int putchar(int с);

Соседние файлы в папке книги