Операции с разрядами (битами) в языке С
Задание 6
- Дополните программу выводом на консоль прочитанного числа из текстового файла.
- В качестве вводимых данных используйте год и месяц рождения пользователя (студента).
- Выполните ротацию шестнадцатеричных чисел, записанных в текстовый файл с именем compX.txt, где Х – номер компьютера, за которым выполняется лабораторная работа. В качестве тестовых чисел и числа битов ротации примите: (0xABCDEF00u, 8), (0xABCDEF00u, –16), (0xFFFF1122u, 4), (0xFFFF1122u, –2), (0xABCDEF00u, 0), (0xABCDEF00u, 44).
- Напишите программу по ротации восьмеричных чисел.
Пример 7. Напишите программу по решению следующего примера. С клавиатуры вводятся два целых числа. Остатки от деления их на 16 заносятся соответственно в 4 младших и 4 старших разряда одного байта. Затем следует напечатать изображение содержимого сформированного байта [16.3].
Программный код решения примера:
#include <stdio.h> #include <conio.h> #include <local.h> setlocale(LC_ALL, ".1251"); int main (void) { int m, n; unsigned char k; //Прототипы функций void binar (unsigned char ch); unsigned char code16 (int a, int b); printf("\n\t Введите первое беззнаковое число N1: "); scanf_s("%d", &m); printf("\t Введите второе беззнаковое число N2: "); scanf_s("%d", &n); k = code16(m, n); printf("\n\t Код двух остатков старших и младших разрядов байта: %u", k); binar(k); printf("\n Нажмите любую клавише (Press any key): "); _getch(); return 0; } unsigned char code16 (int a, int b){ //Объединение с вложенной структурой union { unsigned char z; struct { unsigned int x : 4; //Младшие биты unsigned int y : 4; //Старшие биты } hh; } un; un.hh.x = a % 16; un.hh.y = b % 16; return (un.z); } void binar (unsigned char ch){ int i; // Объединение с вложенной структурой union { unsigned char ss; // Структура с битовыми полями struct { unsigned int a0:1; unsigned int a1:1; unsigned int a2:1; unsigned int a3:1; unsigned int a4:1; unsigned int a5:1; unsigned int a6:1; unsigned int a7:1; } byte; } cod; cod.ss = ch; printf("\n\n\t Число разрядов:\n\t"); for (i = 0; i < 8; ++i) printf("%4d", 7 - i); printf("\n\t Значения битовых полей:\n\t"); printf("%4d%4d%4d%4d%4d%4d%4d%4d",\ cod.byte.a7, cod.byte.a6, cod.byte.a5, cod.byte.a4,\ cod.byte.a3, cod.byte.a2, cod.byte.a1, cod.byte.a0); printf("\n\n"); }
Возможный результат выполнения программы показан на рис. 16.7.
В программе используются две переменных типа объединения и две структурные переменные. Объединение имеет то свойство, что переменные разных типов занимают одну область памяти, соответствующей наибольший размер в байтах. Поэтому если инициализируются одни переменные (например, un.hh.x = a % 16; un.hh.y = b % 16; ), то переменная другого типа ( unsigned char z; ) будет располагаться в той же области памяти, что и переменные типа unsigned int (x и y). В связи с этим якобы неинициализированная переменная un.z возвращается функцией code16(). Размеры полей задаются программистом с учетом того, чтобы в них помещалось соответствующее число, представленное в двоичной системе. Например, если размерность поля равна 2, то в это поле можно записать десятичное число 3, так как его двоичный эквивалент равен 11. В поле с размерностью 8 можно записать число 140, так как его двоичный эквивалент равен 10001100.
Задание 7
- В качестве вводимых чисел примите 2*Х, где Х – номер компьютера, за которым выполняется лабораторная работа.
- Выведите на консоль размерность в байтах объединений и структур, определенных в программе.
- Проанализируйте результат выполнения программы при изменении размера битовых полей в структуре функции code16().
- Видоизмените программу для ввода трех чисел и определения значений разрядных (битовых) полей после занесения в них остатков от деления на целое число 8.
Пример 8. Используя битовые поля структуры, напишите программу вывода на экран дисплея двоичного кода ASCII символа, вводимого с клавиатуры.
Условие примера является классическим, примеры программ приводятся во многих руководствах и учебниках. Ниже приводится некоторая модификация известных программ.
Отметим про наборы символов ASCII. Символы сохраняются в памяти компьютеров с использованием числовых кодов. Часто используется кодировка ASCII (American Standard Code for Information Interchange – американский стандартный код для обмена информацией). Таблицу символов ASCII можно посмотреть в [16.1].
Программный код решения примера:
#include <stdio.h> #include <conio.h> //Шаблон структуры с битовыми полями struct byte { int b1 : 1; int b2 : 1; int b3 : 1; int b4 : 1; int b5 : 1; int b6 : 1; int b7 : 1; int b8 : 1; }; // Определение объединения с вложенной структурой union bits { char ch; struct byte bit; } un;// un - переменная типа объединения // Прототип функции void decode (union bits bt, int ch); // Главная функция int main (void) { printf("\n\t Enter any symbol or Ctrl+Z to quit:\n"); do { printf("\n\t Enter: "); un.ch = getchar(); if ( (un.ch) == EOF) break; decode(un, un.ch); } while ((un.ch = getchar())!= EOF); printf("\n Press any key: "); _getch(); return 0; } // Функция двоичного представления символов void decode (union bits bt, int ch) { printf("\tBinary code of '%c':\n", ch); if (bt.bit.b8) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b7) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b6) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b5) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b4) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b3) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b2) printf("%2c 1", ' '); else printf("%2c 0", ' '); if (bt.bit.b1) printf("%2c 1", ' '); else printf("%2c 0", ' '); printf("\n"); }
Программа ориентирована на 8 бит одного байта целочисленного значения, которым кодируется символ, вводимый с клавиатуры. В функции decode() использовано форматирование на основе символа "пробел".
Возможный результат выполнения программы показан на рис. 16.8.
Задание 8
- Вместо цикла do–while примените другой оператор цикла.
- Напишите программу вывода всех строчных букв латинского алфавита и их двоичных эквивалентов в кодировке ASCII без ввода их с клавиатуры.
Пример 9. Напишите программу левого поразрядного сдвига для вводимого с клавиатуры целого числа с выводом его двоичного эквивалента и с повторными сдвигами влево.
В программе решения примера следует предусмотреть перевод числа из десятичной системы счисления в двоичную систему счисления. При поразрядном сдвиге влево на освободившееся место (места) двоичного числа записываются нули.
Программный код решения примера:
#include <stdio.h> #include <conio.h> // Главная функция int main (void) { long int a; unsigned int m, n; //Прототип функции void dec2(long int var, unsigned int m, unsigned int n); printf("\n\t Enter an integer: "); scanf_s("%ld", &a); _flushall(); printf("\t Enter a value shift: "); scanf_s("%u", &m); _flushall(); printf("\t Enter number of repeated shifts: "); scanf_s("%u", &n); _flushall(); dec2(a, m, n); printf("\n\n Press any key: "); _getch(); return 0; } // Функция поразрядного сдвига void dec2(long int var, unsigned int m, unsigned int n) { unsigned int b, i; long int mask = 1 << 31; long int M[128]; for (i = 0; i < n; ++i) M[i] = var << i*m; printf("\n\t Decimal and its binary equivalent after the shift:\n"); for (i = 0; i < n; ++i) { if (i == 0) { printf("\n Initial number %ld:\n\t", M[0]); for (b = 1; b <= 32; ++b) { printf("%c", M[0] & mask ? '1' : '0'); M[0] <<= 1; // or: var = var << 1; if (b % 8 == 0) putchar(' '); } } else { printf("\n The following number %ld:\n\t", M[i]); for (b = 1; b <= 32; ++b) { printf("%c", M[i] & mask ? '1' : '0'); M[i] <<= 1; if (b % 8 == 0) putchar(' '); } } printf("\n"); } }
В программе предполагалось, что в наличии 32-разрядный компьютер. Кроме того, вывод двоичного эквивалента сделан побайтно, считая, что в одном байте находится 8 бит.
Возможный результат выполнения программы показан на рис. 16.9.
Задание 9
- Проверьте результат выполнения программы с помощью инженерного калькулятора (calc).
- В программу добавьте нумерацию результатов вывода десятичного числа и его двоичного эквивалента.
- Введите число своего дня рождения пользователя.
- Проверьте программу при вводе отрицательных целых чисел.
- Измените программу для поразрядного сдвига вправо.
Контрольные вопросы
- Как осуществляется нумерация разрядов байта?
- Для каких систем счисления в языке С имеются классификаторы форматируемых данных?
- Какие логические поразрядные операции существуют в языке С?
- Какие логические операции сдвига существуют в языке С? Какими операторами они реализуются?
- Что такое битовое поле в языке С? Где оно может быть определено?
- В чем отличие поразрядных и логических операторов НЕ, И и ИЛИ?
- Как можно обменять значения двух целочисленных переменных без использования третьей переменной?
- Чем отличается операция сдвига вправо для типов int и unsigned?