книги / Программирование на языке Си
..pdf322 |
Программирование на языке Си |
в одном байте формируется последовательность битов, приве денная на рис. 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 с);