Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
лабраб_семестр2.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
155 Кб
Скачать
☆

Битовые поля

Стандарт C99, который часто является основой языка С++, позволяет описывать данные на уровне битов. Это достигается путем использования битовых полей, представляющие собой переменные типов signed или unsigned int, у которых используются лишь несколько бит для хранения данных. Такие переменные обычно записываются в структуру и единую последовательность бит. Рассмотрим пример, в котором задается структура flags, внутри которой задано 8 битовых полей:

Теоретические сведения 4

Контрольные вопросы 10

Теоретические сведения 10

Содержание отчета 15

Контрольные вопросы 16

Теоретические сведения 16

Контрольные вопросы 19

Теоретические сведения 20

Контрольные вопросы 23

Содержание отчета 28

Контрольные вопросы 28

Поразрядные операции языка С 28

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

Содержание отчета 35

Контрольные вопросы 35

Работа с текстовыми файлами 35

Работа с бинарными файлами 40

Задание на лабораторную работу 45

Содержание отчета 45

Контрольные вопросы 45

unsigned int sevnth : 1;

unsigned int eighth : 1;

} flags;

Теперь, для определения того или иного бита переменной flags достаточно воспользоваться операцией

flags.first = 1;

flags.third = 1;

В этом случае будут установлены первый и третий биты, а остальные равны нулю, что соответствует числу 5. Данное значение можно отобразить, воспользовавшись функцией printf():

printf("flags = %d.\n",flags);

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

flags = 5; //неверно, так нельзя

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

int var = flags; //ошибка, структуру нельзя присваивать переменной

Так как поля first,..., eighth могут содержать только один бит информации, то они принимают значения 0 или 1 для типа unsigned int и 0 и -1 - для типа signed int. Если полю присваивается значение за пределами этого диапазона, то оно выбирает первый бит из присваиваемого числа.

В общем случае можно задавать любое число бит для описания полей, например

Теоретические сведения 4

Контрольные вопросы 10

Теоретические сведения 10

Содержание отчета 15

Контрольные вопросы 16

Теоретические сведения 16

Контрольные вопросы 19

Теоретические сведения 20

Контрольные вопросы 23

Содержание отчета 28

Контрольные вопросы 28

Поразрядные операции языка С 28

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

Содержание отчета 35

Контрольные вопросы 35

Работа с текстовыми файлами 35

Работа с бинарными файлами 40

Задание на лабораторную работу 45

Содержание отчета 45

Контрольные вопросы 45

} prcode;

Здесь создается два двух битовых поля и одно восьмибитовое. В результате возможны следующие операции присваивания:

prcode.code1 = 0; prcode.code2 = 3; prcode.code3 = 128;

Структуры flags и prcode удобно использовать в условных операторах if и switch. Рассмотрим пример использования структуры битовых полей для описания свойств окна пользовательского интерфейса.

struct tag_window { unsigned int show : 1 //показать или скрыть

unsigned int style : 3 //WS_BORDER, WS_CAPTION, WS_DLGFRAME

unsigned int color : 3 //RED, GREEN, BLUE } window;

Определим следующие константы:

Теоретические сведения 4

Контрольные вопросы 10

Теоретические сведения 10

Содержание отчета 15

Контрольные вопросы 16

Теоретические сведения 16

Контрольные вопросы 19

Теоретические сведения 20

Контрольные вопросы 23

Содержание отчета 28

Контрольные вопросы 28

Поразрядные операции языка С 28

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

Содержание отчета 35

Контрольные вопросы 35

Работа с текстовыми файлами 35

Работа с бинарными файлами 40

Задание на лабораторную работу 45

Содержание отчета 45

Контрольные вопросы 45

#define GREEN 2

#define BLUE 4

#define SW_SHOW 0

#define SW_HIDE 1

Пример инициализации структуры битовых полей

window.show = SW_SHOW;

window.style = WS_BORDER | WS_CAPTION;

window.color = RED | GREEN | BLUE; //белый цвет

Задание на лабораторную работу

  1. Написать программу по работе с поразрядными операциями в соответствии с номером своего варианта.

  2. Написать программу по работе с битовыми полями в соответствии с номером своего варианта.

Варианты заданий

Вариант

Поразрядные операции

Битовые поля

1

Используя операцию «логическое И», выделите первые три бита числа

Написать программу компактного представления календарной даты с соответствующим набором констант

2

Используя операцию «логическое ИЛИ», включите первый и третий биты числа

Написать программу компактного представления свойств окна (цвет, размер, наименование, меню, прозрачность в %) с соответствующим набором констант

3

Используя операцию «логическое НЕ» и «логическое И», выключите пятый и шестой биты числа

Написать программу компактного представления времени (мин:сек:час флаг - am/pm) с соответствующим набором констант

4

Используя операцию «исключающее ИЛИ», переключите 7-й бит числа

Напишите программу работы с 4­битовой переменной и задайте

соответствующий набор констант для работы с ней

5

С помощью поразрядных операций выполните умножение числа 4 на 8

Напишите программу копирования одной 6-ти битовой переменной в другую 6-ти битовую переменную

6

С помощью поразрядных операций выполните деление числа 128 на 4

Напишите программу сортировки массива по возрастанию, состоящего из 4-х битовых переменных

7

Используя операцию «логическое НЕ» и «логическое И», выключите третий и пятый биты числа

Напишите программу сортировки массива по убыванию, состоящего из 6-ти битовых переменных

8

Используя операцию «исключающее ИЛИ», переключите 5-й бит числа

Напишите программу определения максимального значения элемента массива, состоящего из 4-х битовых переменных

9

С помощью поразрядных операций выполните умножение числа 8 на 4

Напишите программу определения максимального значения элемента массива, состоящего из 7-ми битовых переменных

10

С помощью поразрядных операций выполните деление числа 32 на 4

Напишите программу вычисления суммы элементов массива, состоящего из 4-х битовых переменных

Содержание отчета

  1. Титульный лист с названием лабораторной работы, номером варианта, фамилией студента и группы.

  2. Текст программ.

  3. Результаты действия программ.

  4. Выводы по полученным результатам работы программ.

Контрольные вопросы

  1. Дайте понятие битового поля.

  2. С помощью какой поразрядной операции осуществляется выделение битов переменных?

  3. Какая поразрядная операция позволяет переключать биты переменной?

  4. Для каких целей следует использовать битовые поля?

  5. Объясните смысл поразрядной операции «логическое ИЛИ».

  6. С помощью какой поразрядной операции осуществляется деление переменной на 2?

Лабораторная работа №7

РАБОТА С ФАЙЛАМИ

Цель работы: изучить теорию и научиться программировать операции файлового ввода/вывода.

Работа с текстовыми файлами

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

FILE *fopen( const char *filename, const char *mode );

Здесь filename - строка, содержащая путь и имя файла; mode - строка, определяющая режим открытия файла: на чтение или на запись; FILE - специальный тип данных для работы с файлами. Данная функция возвращает значение NULL, если файл не был успешно открыт, иначе - другое значение. Рассмотрим последовательность действий по созданию простого текстового файла на языке C и записи в него текстовой информации.

Листинг 7. Запись текстовой информации в файл.

#include <stdio.h>

int main()

{

char str_file[]="Строка для файла";

FILE* fp = fopen("my_file.txt","w");

if(fp != NULL)

{

printf("Идет запись информации в файл...\n"); for(int i=0;i < strlen(str_file);i++) putc(str_file[i],fp);

}

else printf("Невозможно открыть файл на запись.\n");

fclose(fp);

return 0;

}

В данном примере задается специализированный указатель fp типа FILE, который инициализируется функцией fopen(). Функция fopen() в качестве первого аргумента принимает строку, в которой задан путь и имя файла. Вторым параметром определяется способ обработки файла, в данном случае, значение “w”, которое означает открытие файла на запись с удалением всей прежней информации из него. Если файл открыт успешно, то указатель fp не будет равен NULL и с ним возможна работа. В этом случае с помощью функции putc() выполняется запись символов в файл, на который указывает указатель fp. Перед завершением программы открытый файл следует закрыть во избежание в нем потери данных. Это достигается функцией fclose(), которая принимает указатель на файл и возвращает значение 0 при успешном закрытии файла, иначе значение EOF.

Рассмотрим теперь пример программы считывания информации из файла.

Листинг 8. Считывание текстовой информации из файла.

#include <stdio.h>

int main()

{

char str_file[100];

FILE* fp = fopen("my_file.txt","r"); if(fp != NULL)

{

int i=0; char ch;

while((ch = getc(fp)) != EOF) str_file[i++]=ch; str_file[i] = '\0'; printf(str_file);

}

else printf("Невозможно открыть файл на чтєниє.\n");

fclose(fp);

return 0;

}

В приведенном листинге функция fopen() открывает файл на чтение, что определяется значением второго аргумента равного «г». Это значит, что в него невозможно произвести запись данных, а только считывание. Сначала выполняется цикл while, в котором из файла считывается символ с помощью функции getc() и выполняется проверка: если считанное значение не равно символу конца файла EOF, то значение переменной ch записывается в массив

str_file. Данный цикл будет выполняться до тех пор, пока не будут считаны все

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

#include <stdio.h>

Работа с текстовыми файлами через функции putc и getc не всегда удобна. Например, если необходимо записать или считать строку целиком, то желательно иметь функции, выполняющие эту работу. В качестве таковых можно воспользоваться функциями fputs() и fgets() для работы со строками. Перепишем предыдущие примеры с использованием данных функций.¶int main()

{

char str_file[]="Строка для файла";

FILE* fp = fopen("my_file.txt","w");

if(fp != NULL) fputs(str_file,fp);

else printf("Невозможно открыть файл на запись.\n");

fclose(fp);

fp = fopen("my_file.txt","r");

if(fp != NULL)

{

fgets(str_file,sizeof(str_file),fp);

printf(str_file);

}

fclose(fp);

return 0;

}

Аналогичные действия по записи данных в файл и считывания информации из него можно выполнить и с помощью функций fprintf() и fscanf(). Однако эти функции предоставляют большую гибкость в обработке данных файла. Продемонстрируем это на следующем примере. Допустим, имеется структура, хранящая информацию о книге: название, автор, год издания. Необходимо написать программу сохранения этой информации в текстовый файл и их считывания. Пример использования данных функций представлен в листинге 10.

Листинг 10. Использование функций fprintf() и fscanf().

#include <stdio.h>

#define N 2 struct tag_book {

char name[100];

char author[100];

int year;

} books[N];

int main(void)

{

for(int i=0;i < N;i++)

{

scanf(”%s”,books[i].name);

scanf("%s",books[i].author);

scanf("%d",&books[i].year);

}

for(i=0;i < N;i++)

{

puts(books[i].name);

puts(books[i].author);

printf("%d\n",books[i].year);

}

FILE* fp = fopen("my_file.txt","w"); for(i=0;i < N;i++)

fprintf(fp,"%s %s %d\n",books[i].name,books[i].author, books[i].year);

fclose(fp);

fp = fopen("my_file.txt","r");

for(i=0;i < N;i++)

fscanf(fp,"%s %s %d\n",books[i].name,books[i].author, &books[i].year); fclose(fp);

printf(" \n");

for(i=0;i < N;i++)

{

puts(books[i].name);

puts(books[i].author);

printf("%d\n",books[i].year);

}

return 0;

}

При выполнении данной программы вводится информация по книгам в массив структур books и выводится введенная информация на экран. Затем открывается файл my_file.txt на запись, в который заносится информация по книгам в порядке: наименование, автор, год издания. Так как число книг в данном случае равно двум, то выходной файл будет выглядеть следующим образом:

Onegin Pushkin 1983

Oblomov Griboedov 1985

Затем, файл my_file.txt открывается на чтение и с помощью функции scanf() осуществляется считывание информации в элементы структуры. В заключении считанная информация выводится на экран монитора.

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

При внимательном рассмотрении предыдущих примеров можно заметить, что функции считывания информации из файла «знают» с какой позиции следует считывать очередную порцию данных. Действительно, в последнем примере функция fscanf(), вызываемая в цикле, «знает» что нужно считать сначала первую строку из файла, затем вторую и т.д. И программисту нет необходимости задавать позицию для считывания данных. Все происходит автоматически. Вследствие чего появляется такая особенность работы? Дело в том, что у любого открытого файла в программе написанной на С имеется указатель позиции (номера), с которой осуществляется считывание данных из файла. При открывании файла на чтение номер этой позиции указывает на начало файла. Поэтому функция fscanf(), вызванная первый раз, считывает данные первой строки. По мере считывания информации из файла, позиция сдвигается на число считанных символов. И функция fscanf() вызванная второй раз будет работать уже со второй строкой в файле. Несмотря на то, что указатель позиции в файле перемещается автоматически, в языке С имеются функции fseek() и ftell(), позволяющие программно управлять положением позиции в файле. Синтаксис данных функций следующий:

int fseek( FILE *stream, long offset, int origin ); long ftell( FILE *stream );

где *stream - указатель на файл; offset - смещение позиции в файле (в байтах); origin - флаг начального отсчета, который может принимать значения: SEEK_END - конец файла, SEEK_SET - начало файла; SEEK_CUR - текущая позиция. Последняя функция возвращает номер текущей позиции в файле.

Рассмотрим действие данных функций на примере считывания символов из файла в обратном порядке.

Листинг 11. Использование функций fseek() и ftell().

#include <stdio.h> int main(void)

{

FILE* fp = fopen("my_file.txt","w");

if(fp != NULL)

{

fprintf(fp,"It is an example using fseek and ftell functions.");

}

fclose(fp);

fp = fopen("my_file.txt","r");

if(fp != NULL)

{

char ch;

fseek(fp,0L,SEEK_END);

long length = ftell(fp);

printf("length = %ld\n",length);

for(int i = 1;i <= length;i++)

{

fseek(fp,-i,SEEK_END);

ch = getc(fp);

putchar(ch);

}

}

fclose(fp);

return 0;

}

В данном примере сначала создается файл, в который записывается строка “It is an example using fseek and ftell functions.”. Затем этот файл открывается на чтение и с помощью функции fseek(fp,0L,SEEK_END) указатель позиции помещается в конец файла. Это достигается за счет установки флага SEEK_END, который перемещает позицию в конец файла при нулевом смещении. В результате функция ftell(fp) возвратит число символов в открытом файле. В цикле функция fseek(fp,-i,SEEK_END) смещает указатель позиции на -i символов относительно конца файла, после чего считывается символ функцией getc(), стоящий на i-й позиции с конца. Так как переменная i пробегает значения от 1 до length, то на экран будут выведены символы из файла в обратном порядке.