ЛЕКЦИИ И МАТЕРИАЛ ДЛЯ ЭЭБ и ИСИТвЭ / Методичка по С
.pdfЗдесь addr – шаблон структуры, определенный перед объявлением структуры fulladdr и объявлением переменной а типа структуры fulladdr. Для присвоения значения полю house структуры addres переменной а используем
a.adress.house = 234;
6.2.Доступ к отдельным битам
Вотличие от многих других языков программирования язык С обеспечивает доступ к одному или нескольким битам в байте или слове. В конкретных задачах часто бывает необходимо, чтобы некоторая переменная принимала только два значения. Для этого достаточно использовать только один бит памяти. Такая переменная, по своему содержательному смыслу, является некоторым признаком или флагом.
Один из методов, встроенных в язык С и позволяющий иметь доступ
кбиту, – это поля битов (bit-fields). В действительности поля битов – это специальный тип членов структуры, в котором определено, из скольких бит состоит каждое поле. Основная форма объявления такой структуры следующая:
struct имя_структуры { тип имя1: длина в битах; тип имя2: длина в битах; тип имя3: длина в битах;
. . .
тип имяN: длина в битах; };
В этом объявлении структуры тип может быть одним из следующих: int, unsigned, signed.
Имя1 может быть пропущено, тогда соответствующее количество бит не используется (пропускается ). Длина структуры в целом всегда кратна восьми. Так, если указать
Struct onebit{ Unsigned one_bit: 1; } obj;
Здесь для переменной obj будет выделено 8 бит, но использоваться будет только первый. В структуре также могут быть смешаны обычные переменные и поля битов.
71
6.3.Объединения
Вязыке С определен еще один тип для размещения в памяти нескольких переменных разного типа – объединение( union ). Объявляется объединение также как и структура, например
/* Пример 49 */ union u{
int i; char ch; long int l };
Это объявление задает шаблон объединения. Можно объявить переменные
union u alfa, beta;
Можно было объявить переменные одновременно с заданием шаблона. В отличие от структуры для переменной типа union места в памяти выделяется ровно столько, сколько нужно полю объединения, имеющему наибольший размер в байтах. В приведенном выше примере под переменную alfa будет выделено 4 байта памяти. Действительно, поле i требует 2 байта, поле ch – 1 байт, и поле l – 4 байта. Остальные переменные будут располагаться в том же месте памяти. Синтаксис использования полей объединения такой же, как и для структуры:
u.ch = ‘N’;
Для объединения также разрешена операция ->, если идет обращение к объединению с помощью указателя.
Программа, приведенная ниже, выдает на экран двоичный код символа, вводимого с клавиатуры:
#include < stdio.h > #include < conio > /* Пример 50 */ struct byte{
int b1: 1; int b2: 1; int b3: 1; int b4: 1; int b5: 1;
72
int b6: 1; int b7: 1; int b8: 1; };
union bits{ char ch; struct byte b; } u;
void decode ( union bits b ); main ( void )
{
do { b.ch:=getche(); printf(“;”); decode(u);
} while ( u.ch != ‘q’);
}
void decode ( union bits b )
{
if ( b.byte.b8 ) printf(“1”); else printf(“0”);
if ( b.byte.b7 ) printf(“1”); else printf(“0”);
if ( b.byte.b6 ) printf(“1”); else printf(“0”);
if ( b.byte.b5 ) printf(“1”); else printf(“0”);
if ( b.byte.b4 ) printf(“1”); else printf(“0”);
if ( b.byte.b3 ) printf(“1”); else printf(“0”);
if ( b.byte.b2 ) printf(“1”); else printf(“0”);
if ( b.byte.b1 ) printf(“1”); else printf(“0”); printf(“\n”);
}
73
6.4. Перечислимый тип
Перечислимый тип ( enumeration ) – это множество поименованных целых констант. Перечислимый тип определяет все допустимые значения которые могут иметь переменные этого типа. Основная форма объявления такого типа следующая:
enum имя_типа {список_названий} список переменных;
Список переменных может быть пустым. Пример определения перечислимого типа и переменной данного типа
enum season { win, spr, sum, aut }; enum season s;
Ключом к пониманию сущности перечислимого типа является то, что каждое из имен win, spr, sum, aut представляют собой целую величину. Если эти величины не определены по другому, то по умолчанию они соответсвенно равны нулю, единице, двум и трем. Оператор printf(“%d %d”,win,aut); выдаст на экран числа 0 и 3. Во время объявления типа можно одному или нескольким символам присвоить другие значения, например
enum value { one=1,two,three,ten=10,thousand=1000,next};
Если теперь напечатать значения
printf(“%d %d %d %d %d\n”,one,two,ten,thousand,next);
то на экране появятся числа 1 2 10 1000 1001, т.е. каждый следующий символ увеличивается на единицу по сравнению с предыдущим, если нет другого присваивания.
С переменными перечислимого типа можно производить следующие операции:
–присвоить переменную типа enum другой переменной того же типа;
–провести сравнение с целью выяснения равенства или неравен-
ства;
–арифметические операции с константами типа enum (i=win+aut). Нельзя использовать арифметические операции и операции ++ и –
для переменных типа enum.
Основная причина использования перечислимого типа – улучшение читаемости программ.
74
6.5. Переименование типов
Язык С позволяет дать новое название уже существующим типам данных. Для этого используется ключевое слово typedef. При этом создается новый тип данных. Например:
typedef char SYMBOL; typedef unsigned UNSIGN; typedef float real;
Достаточно часто используется оператор typedef с применением структур:
/* Пример 51 */
typedef struct st_tag{ char name[40];
int kurs;
char group[5]; int stip;
} STUDENT;
Теперь для определения переменной можно использовать
struct st_tag avar;
А можно использовать
STUDENT avar;
75
7.ВВОД/ВЫВОД И РАБОТА С ФАЙЛАМИ
7.1.Организация ввода-вывода
ВязыкеСотсутствуютспециальныеоператорыввода-вывода.Вседействия, связанные с вводом-выводом, выполняются с помощью функций библиотеки
С.Программист может также создать свои собственные функции ввода-вывода на основе библиотечных. При вводе-выводе все данные рассматриваются как поток байтов, связанный либо с файлом на диске, либо с нефайловым физическим устройством (клавиатура, экран монитора, принтер и т. п.). Функции вво- да-вывода позволяют выделять из потока и обрабатывать данные различных форматов и размеров, обеспечивая при этом буферизированный форматированный или неформатированный ввод или вывод.
Для использования функций ввода-вывода необходимо директивой #include включить файл stdio.h, содержащий объявления функций ввода-вывода, а также определение констант, типов и структур, используемых этими функциями. Открытие потока осуществляется функцией fopen. При успешном открытии эта функция возвращает указатель структуры типа FILE, которая содержит информацию, необходимую для работы с потоком. При ошибке открытия возвращается значение NULL, которое определено как константа в stdio.h. Указатель потока используется в дальнейшем во всех функциях вашей программы, работающих с данным потоком. Количество одновременно открытых потоков ограничевается установками операционной системы.
Параметрами функции fopen являются строка, указывающая путь к файлу и его имя, и строка, определяющая тип доступа к потоку. Литерал типа доступа может иметь значения: “r” – для чтения, “w” – для записи, “a” - для записи в конце потока, “r+” – для чтения и записи, “w+” – пустой поток для чтения и записи, “a+” – для чтения и записи в конце потока. Закрытие потока осуществляется функцией fclose. Указатель потока можно позиционировать на любое место в потоке. Для получения текущей позиции в потоке используются функции ftell и fgetpos, для изменения позиции указателя – fseek и fsetpos. В stdio.h определены стандартные указатели потоков: stdin – стандартный ввод(клавиатура), stdout – стандартный вывод(дисплей), stdprn – стандартная печать, stderr
76
– стандартный вывод сообщений об ошибках. Эти указатели можно использовать во всех функциях , требующих указатель файла, без предварительного открытия потока с помощью функции fopen. Кроме того, функции ввода-выво- да на терминал (getc, putc и т.п.) не требуют указатель потока, так как используют stdin и stdout. Существуют также функции, реализующие ввод-вывод в текущее окно экрана монитора путем прямой записи в видеопамять ( getch, getche, putch, cprintf и т.д.). Эти функции требуют подключения директивой #include файла conio.h.
7.2. Классификация функций чтения и записи
Классификация функций чтения и записи представлена в табл. 7.1.
Таблица 7.1
Классификация функций чтения и записи
|
|
Чтение |
|
Возвращаемое значение |
||
Объект операции |
|
|
|
|
|
|
Из потока |
Из любого |
Из строки |
При успешном |
При |
||
|
||||||
|
stdin |
потока |
С |
завершении |
ошибке |
|
|
|
|
|
|
|
|
Последовательность |
|
fread |
|
Количество |
0 |
|
байт |
|
|
элементов |
|||
|
|
|
|
|||
|
|
|
|
|
|
|
Отдельный символ |
fgetchar |
fgetc getc |
|
Введенный |
EOF |
|
|
getchar |
|
|
символ |
|
|
Данное типа int |
|
getw |
|
Введенное |
EOF |
|
|
|
число |
||||
|
|
|
|
|
||
|
|
|
|
|
|
|
Строка |
gets |
fgets |
|
Строка |
NULL |
|
|
|
|
|
|
|
|
Форматированные |
|
|
|
Количество |
|
|
scanf |
sscanf |
|
введенных |
EOF |
||
данные |
|
|||||
|
|
|
полей |
|
||
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
Запись |
|
Возвращаемое значение |
||
Объект операции |
|
|
|
|
|
|
Из потока |
Из любого |
Из строки |
При успешном |
При |
||
|
||||||
|
stdin |
потока |
С |
завершении |
ошибке |
|
|
|
|
|
|
|
|
Последовательность |
fputchar |
fputc putc |
|
Выведенный |
EOF |
|
байт |
putchar |
|
символ |
|||
|
|
|
|
|
|
|
Отдельный символ |
|
putw |
|
Выведенное |
EOF |
|
|
|
число |
||||
|
|
|
|
|
||
|
|
|
|
|
|
|
Строка |
puts |
fputs |
|
Последний |
EOF |
|
|
символ |
|||||
|
|
|
|
|
||
|
|
|
|
|
|
|
Форматированные |
|
|
|
Количество |
|
|
printf |
fprintf |
sprintf |
выведенных |
EOF |
||
данные |
||||||
|
|
|
байт |
|
||
|
|
|
|
|
||
|
|
|
|
|
|
|
77
Рассмотрим пример программы, которая выводит содержимое файла autoexec.bat в стандартный поток вывода, а также выводит на экран монитора последнии 15 байт этого файла.
#include <stdio.h> #include <conio.h> /* Пример 52 */ main(void)
{
FILE *f; int a; long int n;
if((f =fopen(“c:\\autoexec.bat”,”r”))==NULL) /*открываем поток f */ { prinf(“ Ошибка при открытии файла\n”); exit(1); } while((a=fgetc(f)) != EOF /* пока не конец файла читаем в память*/
fputc(a,stdout); /* и выводим в стандартный поток вывода*/ n=-15;
fseek(f,n,SEEK_END); /* позиционирование за 15 байт до конца*/ while((a=fgetc(f)) != EOF) /* пока не конец файла читаем в память*/
putchar(a); /* и выводим на экран монитора*/ fclose(f); /* закрываем поток*/
getch();
}
7.3. Функции библиотеки ввода-вывода
Библиотека функций ввода-вывода, которые можно использовать при программировании на языке С, весьма разнообразна, что определяет необходимость привести ее полностью.
7.4. Функции для работы с файлами
fopen – открывает поток, связанный с файлом filename и типом доступа type.
FILE *fopen(char *filename, char *type); fclose – закрывает поток stream.
int fclose(FILE *stream);
fcloseall – закрывает все открытые потоки. int fcloseall( void );
remove – удаляет файл с именем filename. int remove( char *filename );
rename – переименовывает файл oldname в файл newname.
78
int rename(char *oldname, char *newname);
ftell – возвращает положение указателя текущей позиции файла, связанного с потоком stream. Значение возвращается в виде смещения в байтах относительно начала файла. Значение, возвращаемое функцией ftell, в дальнейшем можно использовать при вызове функции fseek. ftell возвращает положение указателя текущей позиции при успешном завершении.При ошибке возвращается значение – 1L.
long int ftell( FILE *stream);
fseek – устанавливает адресный указатель файла, соответствующий потоку stream, в новую позицию, которая расположена по смещению offset относительно места в файле, определенного параметром fromtwhere. Параметр fromtwhere может иметь одно из трех значений 0, 1 или 2, которые представлены тремя символическими константами, определенными в файле stdio.h, следующим образом:
SEEK_SET(0) – начало файла, SEEK_CUR(1) – позиция текущего указателя файла, SEEK_END(2) – конец файла(EOF); Функция fseek возвращает значение 0, если указатель файла успешно перенесен, и ненулевое значение в случае неудачного завершения.
int fseek(FILE *stream, long int offset, int fromwhere);
fgetpos – сохраняет позицию указателя файла, связанного с потоком stream, в месте, указываемом параметром pos. При успешном завершении fgetpos возвращает 0.
int fgetpos(FILE *stream, fpos_t *pos);
Здесь и далее fpos_t - предварительно объявленный тип typedef long fpos_t.
fsetpos - устанавливает указатель текущей позиции файла, связанного с потоком stream в новую позицию, которая определяется значением, получаемым предшествующим вызовом функции fgetpos. При успешном завершении fsetpos возвращает 0.
Int fsetpos( FILE *stream, const fpos_t *pos);
7.5. Функции неформатированного ввода-вывода
fgetc – получает символ из потока stream. int fgetc(FILE *stream);
fgetchar – получает символ из потока stdin. int fgetchar( void );
79
fgets – получает строку s длиной не более n символов из потока stream.
char *fgets(char *s, int n, FILE *stream); fputc – выводит символ с в поток stream. int fput(int c, FILE *stream);
fputcharвыводит символ c в поток stdout. int fputchar(int c);
fputs – выводит строку символов string в поток stream. int fputs(char *string, FILE *stream);
gets – получает строку символов s из потока stdin. char *gets(char *s);
getc – выводит из потока stream символ этого потока. int getc(FILE *stream);
getchar – выводит символ из потока stdin. int getchar( void );
putc – выводит символ c в поток stream. int putc(int c, FILE *stream);
putchar – выводит символ с в поток stdout. int putchar(int c);
puts – выводит строку s в поток stdout. int puts(const char *s);
putw – помещает в поток stream целое значение w. int putw(int w, FILE *stream);
getw – вводит из потока stream целое число. int getw(FILE *stream);
7.6. Функции блочного ввода-вывода
fread – считывает n элементов данных длиной size из потока stream по адресу ptr.
size_t fread(void *ptr, size_t size, size_t n, FILE *stream );
Здесь и далее size_t – предварительно объявленный в библиотеке тип typedef usigned size_t.
fwrite – записывает n элементов данных длиной size из ptr и поток stream.
Size_t fwrite( void *ptr, size_t size, size_t n, FILE *stream);
80
