Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.doc
Скачиваний:
10
Добавлен:
19.09.2019
Размер:
8 Мб
Скачать

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

  1. Какие операции над структурами разрешены в языке С?

  2. Как осуществляется передача частей структуры в качестве аргументов функции?

  3. Как осуществляется возврат частей структуры из функции?

  4. Как реализуется возвращение измененной структуры из функции?

  5. Как осуществляется обращение к полям структуры, переданной функции в виде аргумента?

  6. Допустимо ли объявление переменных глобальной структуры и переменных структуры функции одними и теми же идентификаторами?

  7. Как распределить структуру в динамической памяти?

  8. Как осуществляется возврат данных структурного типа из пользовательской функции?

Библиографический список

  1. Керниган Б. У. Язык программирования С : пер. с англ./Б. У.Керниган, Д. М.Ритчи. – 2-е изд. – М.: Вильямс, 2007. – 304 с.

  2. Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. – М.: Вильямс, 2007. – 704 с.

  3. Прата С. Язык программирования С. Лекции и упражнения : пер. с англ. / С. Прата. – 5-е изд. – М.: Вильямс, 2006. – 960 с.

  4. Пантелеев А. В. Теория функций комплексного переменного и операционное исчисление в примерах и задачах: учеб. пособие /А.В. Пантелеев, А.С. Якимов. – М.: Высш. шк., 2001. – 445 с.

  5. Дейтл Х.М. Как программировать на С : пер. с англ./Х.М. Дейтл, П.Дж. Дейтл. – 4-е изд. – М. : Бином-Пресс, 2006. – 912 с.

Тема 16 Операции с разрядами (битами) в языке с

Рассматриваются операторы и операции по управлению отдельными разрядами (битами) переменных, а также операции с битовыми полями.

ТЕОРЕТИЧЕСКАЯ ЧАСТЬ

1.1. Базовые системы счисления

В языке программирования С предусмотрено управление отдельными разрядами (битами) значения переменной. Эта возможность связывается с представлением числа в компьютере, а именно с различными системами счисления: двоичной, восьмеричной, шестнадцатеричной.

У бита возможны только два значения: 0 и 1. Техническую реализацию таких состояний легко представить, например включено–выключено, положительное значение напряжения–отрицательное значение напряжения (определенного уровня) и т.д.

В языке программирования С термин байт используется для обозначения размера (разрядности) хранения набора символов. Поэтому в языке С байт может содержать 8, 9, 16 и другое количество разрядов. Однако в характеристиках модулей памяти и систем передачи данных предполагается, что байт содержит восемь разрядов [1]. Разряды байта пронумерованы справа налево числами от 0 до 7. Седьмой разряд (крайний левый) называется старшим, а нулевой (крайний правый) – младшим.

Байт имеет наибольшее значение, когда все его разряды установлены, т.е. имеют значение 1. Например, для 8 разрядов с учетом двоичной системы счисления в случае возможного наибольшего значения получим:

128

64

32

16

8

4

2

1

=

255

27

26

25

24

23

22

21

20

=

255

Наименьшему значению соответствует комбинация нулей 00000000, которая представляет собой просто нуль [9].

Байт может хранить числа от 0 до 255, что составляет 256 возможных значений. Программа может интерпретировать комбинацию разрядов иначе и применять байт для хранения чисел от –128 до 127, (также 256 значений). Например, тип unsigned char обычно характеризуется использованием байта для представления чисел в диапазоне от 0 до 255, тип signed char – до 127.

В основании восьмеричной системы счисления лежит число 8 (23). Каждое знакоместо восьмеричного числа соответствует определенной степени восьми. Для записи используются цифры от 0 до 7. Каждая цифра восьмеричного числа соответствует трем двоичным цифрам. Двоичные эквиваленты таких цифр представлены в табл.16.1.

Таблица 16.1

Двоичные эквиваленты восьмеричных цифр

Восьмеричная цифра

Двоичный эквивалент

0

1

2

3

4

5

6

7

000

001

010

011

100

101

110

111

В шестнадцатеричной системе счисления используются степени числа 16 и цифры от 0 до 15. Для представления цифр, соответствующих десятичным значениям от 10 до 15, буквы буквы от A до F [1]. Например, в шестнадцатеричном числе A3F (в языке С записывается как 0xA3F) заложено следующее значение:

10 162 + 3 161 + 15 160 = 2623 (по основанию 10).

В приведенной записи числу 10 соответствует А, а числу 15 – F.

Язык С допускает использование букв нижнего и верхнего регистра (строчных и прописных) для обозначения шестнадцатеричных цифр. Таким образом, число 2623 в указанной системе счисления можно записать как 0xA3F, так и 0xa3f. Каждая цифра шестнадцатеричного числа соответствует 4-значному двоичному числу [1], следовательно, две шестнадцатеричные цифры – одному восьмиразрядному байту. Первая цифра представляет 4 старших разряда, а вторая – 4 младших.

Соответствие между шестнадцатеричными цифрами, десятичными и двоичными числами показано в табл.16.2.

Таблица 16.2

Эквиваленты шестнадцатеричных чисел

Десятичное число

Шестнадцатеричная цифра

Двоичный эквивалент

1

2

3

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

7

9

0000

0001

0010

0011

0100

0101

0110

0111

1000

1001

Окончание табл.16.2

1

2

3

10

11

12

13

14

15

A

B

C

D

E

F

1010

1011

1100

1101

1110

1111

В языке С существуют два средства управления разрядами [1]. Первое представляет собой набор поразрядных операций, а второе – форму полей данных, которая гарантирует доступ к разрядам значения типа int.

Далее будут использоваться 8-разрядные числа в двоичной системе счисления.

1.2. Логические поразрядные операции

В языке программирования С существуют два вида поразрядных операций: логические и операции сдвига [1]. Поразрядные операции выполняются над каждым разрядом независимо от того, находится он слева или справа, а также над целыми числами. Рассмотрим логические поразрядные операции.

Поразрядное отрицание обозначается символом тильда (~). Унарная операция ~ преобразовывает все единицы в нули и все нули в единицы (предполагается, что операции производятся над двоичными числами). Ее называют также операцией «дополнение», т.е. все биты, равные 0, переводятся в 1, а когда все биты, равные 1, – в 0.

Поразрядная операция И обозначается символом &. Двоичная операция & создает новое значение за счет выполнения поразрядного сравнения двух операндов. Для каждой позиции результирующий разряд будет иметь значение 1 только в случае, когда соответствующие разряды обоих операндов имеют значение 1. Можно сказать, что когда над двумя значениями производится операция поразрядного умножения &, то двоичные представления чисел сравниваются бит за битом [2]. Например, пусть одна переменная w1 есть число 25, а другая w2 – число 77. Соответственно в двоичном представлении 25 = 0000000000011001, 77 = 0000000001001101. Тогда в результате поразрядной операции & получим число w3 = w1 & w2. Результат действия оператора & можно представить следующим образом:

Поразрядная операция И (&)

w1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 (25)

w2 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 (77)

w3 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 (9)

В результате поразрядной операции & над двумя числами (25 и 77 в двоичном представлении), которые имеют, например, 16 бит, получили новое число 9.

Поразрядный оператор & часто используется для операций маскирования. С его помощью принудительно можно установить определенный бит данных в значение 0. Например, в утверждении

w3 = w1 & 3;

переменной w3 присваивается значение переменной w1, для которой выполнена поразрядная операция & со значением 3. При этом все биты левее двух младших битов устанавливаются в 0, а остальные сохраняют свое значение [2]. Маска представляет собой некоторую комбинацию разрядов [1]. Разряды маски с нулевыми значениями можно считать аналогом непрозрачных ячеек реальной маски, а разряды со значениями 1 – прозрачными ячейками.

Поразрядная операция & называется также конъюнкцией, или логическим умножением, часто обозначается словом AND.

Поразрядная операция ИЛИ обозначается символом |. Когда над двумя значениями производится операция, то последовательно сравниваются значения всех битов при их двоичном представлении [5]. Если соответствующий бит имеет значение 1 в первом или втором операнде, то результирующее значение будет равно 1. Рассмотрим предыдущий пример с поразрядной операцией ИЛИ:

Поразрядная операция ИЛИ (|)

w1 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 (25)

w2 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 1 (77)

w3 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 1 (93)

Данную операцию обычно используют для установки заданных битов слова в 1. Ее называют также включающей дизъюнкцией, или логическим сложением. Часто применяется обозначение OR.

Поразрядная операция исключающего ИЛИ (^) работает следующим образом [5]. Сравниваются соответствующие биты двух операндов, и если только один из битов равен 1, будет получен результат 1, а при равенстве обоих соответствующих битов или 0, или 1 результат будет равен 0. Для двух операндов b1, b2 при использовании исключающего ИЛИ справедлива таблица истинности (табл.16.3).

Таблица 16.3

Таблица истинности операции исключающего ИЛИ

b1

b2

b1 ^ b2

0

0

0

0

1

1

1

0

1

1

1

0

Если операцию исключающего ИЛИ использовать для одного и того же значения, то в результате получится нуль. Этот прием часто использовался программистами в языке ассемблера как наиболее быстрый путь установить значение в нуль или сравнить два значения на их равенство. Данный способ не рекомендуется использовать в языке программирования С, так как скорость работы не повышается, а программа становится менее понятной [2]. Операция исключающего ИЛИ может применяться для перестановки значений двух переменных без выделения дополнительной памяти (и соответственно без использования дополнительной переменной).

Поразрядная операция исключающего ИЛИ называется также исключающей дизъюнкцией, применяется обозначение XOR.

1.3. Поразрядные операции сдвига

Оператор сдвига влево <<

Когда оператор сдвига влево << выполняется над некоторым значением, все биты, составляющие это значение, сдвигаются влево [2]. Связанное с данным оператором число показывает количество бит, на которое значение должно переместиться. Биты, сдвигающие со старшего разряда, считаются потерянными, а на место младших битов всегда помещаются нули.

Оператор сдвига вправо >>

Операция сдвига вправо >> сдвигает разряды левого операнда вправо на количество позиций, указываемое правым операндом. Выходящие за правую границу разряды теряются. Для типов данных без знака (unsigned) освобождаемые слева позиции заполняются нулями. Для знаковых типов результат зависит от используемой системы. Освобождаемые позиции могут заполняться нулями либо копиями знакового (первого слева) разряда [1].

Поразрядные операции сдвига могут служить удобным и эффективным средством выполнения операций умножения и деления на числа, представляющие собой степени двойки [1]. Они аналогичны смещению десятичной точки при умножении или делении на 10.

1.4. Битовые поля

Второй метод управления разрядами состоит в использовании битового (разрядного) поля [1], которое представляет собой последовательную цепочку разрядов в рамках значения типа signed int или unsigned int. Оно может быть только элементом структуры или объединения [3]. Создается путем объявления структуры (объединения), которая помечает каждое поле и определяет его разряд.

Приведем пример с использованием битовых полей в структуре [2]:

struct packed_struct {

unsigned int : 3;

unsigned int f1 : 1;

unsigned int f2 : 1;

unsigned int f3 : 1;

unsigned int type : 8;

unsigned int index : 18; };

В созданном шаблоне-структуре с дескриптором packed_struct первый член не имеет имени. Символ :3 задает три безымянных бита. Второй, третий и четвертый члены структуры – f1, f2, f3 также имеют тип unsigned int. Символ :1 говорит о том, что в данном члене структуры будет храниться 1 бит. Член структуры с именем type в памяти занимает 8 бит, член структуры index рассчитан на хранение 18 бит.

Для заданного шаблона структуры можно определить структурную переменную, например

struct packed_struct packed_data;

После этого можно присваивать значения полям структуры:

packed_data.type = 7;

Если ранее была объявлена какая-то переменная, например n, то присвоение может быть таким:

packed_data.type = n;

При этом нет необходимости беспокоиться о том, что значение переменной n будет слишком большим. Только младшие 8 бит будут учитываться при присваивании значения для поля packed_data.type. Для извлечения битовых полей структуры можно использовать обычное утверждение:

n = packed_data.type;

После извлечения значения поля type будет произведен сдвиг в сторону младших бит.

Битовые поля могут быть объявлены только как тип int (в стандарте С99 также _Bool), от реализации которого зависит, будет он знаковым (signed) или беззнаковым (unsigned). Для исключения неоднозначности следует использовать явные объявления: signed int или unsigned int. Битовые поля нельзя объединять в массивы, нельзя использовать адрес битового поля, поэтому не может быть такого типа, как «указатель на битовое поле» [2]. Компилятор языка программирования С не переупорядочивает битовые поля для получения оптимального распределения памяти. Но в некоторых случаях может производиться выравнивание за счет безымянного поля. Это может использоваться для выравнивания следующего поля структуры по границе блока [2].

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

ПРАКТИЧЕСКАЯ ЧАСТЬ

Пример1. Написать программу по демонстрации операции поразрядного отрицания (поразрядного дополнения) числа без знака, вводимого с клавиатуры, с использованием операций побитового сдвига.

Для решения примера зададим какое-то число, которое представим в виде нескольких разрядов, после чего через операции побитового сдвига изменим его и выведем на консоль.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

// Прототип функции

void printBits(unsigned int var);

// Главная функция

int main (void) {

unsigned int number;

printf("\n The program on demonstration digit-by-digit operation of denying ( ~ )\n");

printf("\n\t Enter a whole number of unsigned: ");

scanf_s("%u", &number);

printf("\n\t Binary representation of the starting number and\n");

printf("\t Binary representation of bitwise negation of the initial number:\n");

printBits(number); //Исходное число

printBits(~number); // Число после поразрядного дополнения

printf("\n\n Press any key: ");

_getch();

return 0;

}

// Функция побитового представления целого числа без знака

void printBits(unsigned int var)

{

unsigned int b;

unsigned int mask = 1 << 31; // shift to 31 bit

printf("\n\t %10u = ", var);

for (b = 1; b <= 32; ++b) {

printf("%c", var & mask ? '1' : '0');

var <<= 1; // or: var = var << 1;

if (b % 8 == 0)

putchar(' ');

}

}

В программе применен форматный ввод числа в виде %u и вывод числа в виде %10u, где u применяется для беззнакового типа числа, 10 – количество позиций, отводимое для десятичного числа. Предполагается, что заданное число может быть представлено 32 разрядами: по 4 группы с 8 разрядами (4 байта по 8 бит в каждом). Применение оператора сдвига (<<) позволяет все биты числа сдвигать на единицу (действие в цикле for) влево. При этом используется операция поразрядного И. С помощью оператора условия (?) осуществляется замена 1 на 0 и наоборот.

В озможный результат выполнения программы показан на рис.16.1. Видно, что все нули или единицы исходного числа (в двоичном представлении) были инвертированы.

Рис.16.1. Поразрядное инвертирование целого числа

Задание1

  1. Проверьте результат выполнения программы с помощью инженерного калькулятора calc операционной системы Windows.

  2. Выполните раздельное объявление и раздельное определение (инициализацию с поразрядным сдвигом) переменной mask.

  3. В теле цикла for вместо функции printf() примените putchar().

  4. Дополните программу выводом результатов в файл compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

  5. В качестве стартового числа примите год рождения пользователя.

Пример2. Написать программу выполнения поразрядной операции И над двумя целыми числами, представленными в двоичной системе счисления для 32-разрядного компьютера.

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

Программный код решения примера

#include <stdio.h>

#include <conio.h>

// Прототип функции

void printBits(unsigned int var);

// Главная функция

int main (void)

{

unsigned int number, mask;

printf("\n The program on demonstration digit-by-digit operation And ( & )\n");

printf("\n\t Enter a whole number of unsigned: ");

scanf_s("%u", &number);

printf("\t Enter the number of unsigned-mask: ");

scanf_s("%u", &mask);

printf("\n\t Binary representation of the starting number and (%u) and\n", number);

printf("\t Binary representation of the number-masks (%u):\n", mask);

printBits(number);

printBits(mask);

// Число после поразрядного умножения (И)

printBits(number & mask);

printf("\n\n Press any key: ");

_getch();

return 0;

}

// Функция побитового представления целого числа без знака

void printBits(unsigned int var) {

unsigned int b;

unsigned int mask = 1 << 31; // shift to 31 bit

printf("\n\t %10u = ", var);

for (b = 1; b <= 32; ++b) {

printf("%c", var & mask ? '1' : '0');

var <<= 1; // or: var = var << 1;

if (b % 8 == 0)

putchar(' ');

}

}

В программе операция поразрядного умножения И закладывается в фактический параметр функции printBits().

Возможный результат выполнения программы представлен на рис.16.2.

Рис.16.2. Результат поразрядного оператора И над двумя числами

Как видно из рис.16.2, битовая единица первого числа «проходит» в результат, если в маске на этом же месте (в том же разряде) также находится битовая единица. В результате получается новое число – 32.

Задание2

  1. В качестве стартового числа примите год рождения пользователя, а в качестве маски – число рождения.

  2. Примените операцию поразрядного умножения к введенным отрицательным числам и выведите результат на консоль. Сравните с тем, что выводит функция printBits().

  3. Вместо оператора условия ? примените другой способ вывода 1 или 0 после выполнения поразрядной операции И.

  4. Консольный вывод запишите в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

Пример3. Написать программу демонстрации поразрядной операции ИЛИ.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

// Прототип функции

void printBits(unsigned int var);

// Главная функция

int main (void)

{

unsigned int number1, number2;

printf("\n The program on demonstration digit-by-digit operator OR ( | ):\n");

printf("\n\t Enter the unsigned number of N1: ");

scanf_s("%u", &number1);

printf("\t Enter the unsigned number of N2: ");

scanf_s("%u", &number2);

printf("\n\t Binary representation of the number of N1 (%u) and\n", number1);

printf("\t Binary representation of the number of N2 (%u):\n", number2);

printBits(number1);

printBits(number2);

// Число после поразрядного сложения (ИЛИ)

printBits(number1 | number2);

printf("\n\n Press any key: ");

_getch();

return 0;

}

// Функция побитового представления целого числа без знака

void printBits(unsigned int var)

{

unsigned int b;

unsigned int num = 1 << 31;

printf("\n\t %10u = ", var);

for (b = 1; b <= 32; ++b)

{

printf("%c", var & num ? '1' : '0');

var <<= 1; // or: var = var << 1;

if (b % 8 == 0)

putchar(' ');

}

}

В программе, как и в предыдущем примере, использована функция printBits(). Она при необходимости преобразует десятичные числа в двоичные. Входным параметром функции является поразрядное включающее ИЛИ над двумя операндами в главной функции main().

В озможный результат выполнения программы показан на рис.16.3.

Рис.16.3. Результат поразрядного включающего оператора ИЛИ

Как видно из полученного результата, поразрядная операция ИЛИ представляет собой побитовое сложение чисел при условии, что 1 + 1 = 1.

Задание3

  1. В качестве исходных чисел примите год и число рождения пользователя.

  2. Произведите операцию включающего ИЛИ над двумя десятичными числами в соответствии с предыдущим пунктом задания. Операцию выполните в главной функции main().

  3. Видоизмените программу для случая, когда имеется 16-разрядный компьютер.

Пример4. Написать программу демонстрации поразрядного исключающего ИЛИ.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

// Прототип функции

void printBits(unsigned int var);

int main (void)

{

unsigned int number1, number2;

printf("\n The program on demonstration digit-by-digit excluding operator OR ( ^ ):\n");

printf("\n\t Enter the unsigned number of N1: ");

scanf_s("%u", &number1);

printf("\t Enter the unsigned number of N2: ");

scanf_s("%u", &number2);

printf("\n\t Binary representation of the number of N1 (%u) and\n", number1);

printf("\t Binary representation of the number of N2 (%u):\n", number2);

printBits(number1);

printBits(number2);

printBits(number1 ^ number2); // Число после операции ^

printf("\n\n Press any key: ");

_getch();

return 0;

}

// Функция побитового представления целого числа без знака

void printBits(unsigned int var)

{

unsigned int b;

unsigned int num = 1 << 31;

printf("\n\t %10u = ", var);

for (b = 1; b <= 32; ++b)

{

printf("%c", var & num ? '1' : '0');

var <<= 1; // or: var = var << 1;

if (b % 8 == 0)

putchar(' ');

}

}

В озможный результат выполнения программы приведен на рис.16.4.

Рис.16.4. Результат поразрядного исключающего оператора ИЛИ

Задание4

  1. В качестве исходных десятичных чисел примите год и число рождения пользователя.

  2. Произведите операцию исключающего ИЛИ над двумя десятичными числами в соответствии с предыдущим пунктом задания. Операцию выполните в главной функции main().

  3. Подсчитайте число итераций оператора цикла for.

Пример5. Написать программу выполнения поразрядных операций с шестнадцатеричными числами, которые получаются после преобразования десятичных чисел, введенных с клавиатуры.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

const char basestr[16] =

{'0','1','2','3','4','5','6','7','8','9',

'A','B','C','D','E','F'};

const char *two16[16] =

{"0000","0001","0010","0011","0100","0101","0110","0111",

"1000","1001", // 0,1,2,3,4,5,6,7,8,9

"1010","1011","1100","1101","1110","1111"}; //A,B,C,D,E,F

// Прототипы функций

char *dec2hex(unsigned int var);

void printHex2Bin(unsigned int number);

// Главная функция

int main (void)

{

int d12;

unsigned int number1, number2;

char ch, *NUM1;

printf("\n The program on demonstration digit-by-digit operations\n with hexadecimal numbers\n");

printf("\n\t Enter the unsigned number of N1: ");

scanf_s("%u", &number1);

printf("\t Enter the unsigned number of N2: ");

scanf_s("%u", &number2);

_flushall();

//Для контроля перевода десятичного числа в шестнадцатеричное

printf("\n\t Hexadecimal numbers: %X, %X\n", number1,number2);

printf("\n\t Enter bit operation ( &, |, ^, ~ ): ");

scanf_s("%c", &ch);

switch (ch) {

case '&':

NUM1 = dec2hex(number1 & number2);

printHex2Bin(number1 & number2);

break;

case '|':

NUM1 = dec2hex(number1 | number2);

printHex2Bin(number1 | number2);

break;

case '^':

NUM1 = dec2hex(number1 ^ number2);

printHex2Bin(number1 ^ number2);

break;

case '~':

printf("\t Type 1 - for the first number; type 2 - for the second number: ");

scanf_s("%d", &d12);

_flushall();

if (d12 == 1) {

NUM1 = dec2hex(~number1);

printHex2Bin(~number1); }

else {

NUM1 = dec2hex(~number2);

printHex2Bin(~number2); }

break;

default :

fprintf(stdout, "\n\t Unknown symbol.");

break;

}

printf("\n\n Press any key: ");

_getch();

return 0;

}

char *dec2hex(unsigned int var) {

int i, j, number[64], k = 0;

unsigned int base = 16;

char Hex[128], *PTR = Hex;

do {

number[k] = var % base;

++ k;

var /= base;

} while (var != 0);

for (i = k-1, j = 0; i >= 0; --i, ++j) {

Hex[j] = (char )basestr[number[i]];

PTR[j] = Hex[j];

}

PTR[j] = '\0';

return (PTR);

}

void printHex2Bin(unsigned int number) {

unsigned int i, num = 1 << 31;

printf("\n Hexadecimal number: %s\n", dec2hex(number));

printf(" Binary equivalent:\n ");

for (i = 1; i <= 32; ++i) {

printf("%2c", (number & num )? '1' : '0');

number <<= 1;

if (i % 8 == 0) printf(" ");

}

}

В программе используется функция

char *dec2hex(unsigned int var);

Это дает возможность возвращать указатель с адресом необходимого символьного массива. Обратите внимание на выполнение операции завершения строки в функции *dec2hex() для указателя *PTR.

В озможный результат выполнения программы представлен на рис.16.5.

Рис.16.5. Пример побитовых операций с шестнадцатеричными числами

Задание5

  1. В программе предусмотрите вывод двоичных эквивалентов для введенных чисел.

  2. Измените программу так, чтобы операции с переключателем switch выполнялись в функции printHex2Bin().

  3. Создайте функцию и сформируйте в ней одномерный массив символов двоичного эквивалента заданного шестнадцатеричного числа. Предусмотрите вывод результата.

  4. Выполните программу со всеми поразрядными операциями при вводе чисел (года и дня рождения пользователя).

  5. Напишите программу перевода десятичных чисел в восьмеричные (вместо *dec2hex()). Предусмотрите также поразрядные операции с выводом двоичных эквивалентов.

  6. В программе предусмотрите копирование консольного содержания в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа.

Пример6. Написать программу ротации битов для заданных чисел, представленных в шестнадцатеричном виде, когда биты с самых крайних разрядов не теряются, а переносятся на противоположную сторону [2].

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <string.h>

const char basestr[16] =

{'0','1','2','3','4','5','6','7','8','9',

'A','B','C','D','E','F'};

int main (void) {

int n;

unsigned int number, uns;

char *NUM1, str[128] = "0x", un[] = "u";

FILE *fid;

// Прототипы функций

char *dec2hex (unsigned int var);

unsigned int rotate (unsigned int value, int n);

printf("\n The program for demonstration of bit-by-bit rotation \n of the numbers set in a hexadecimal kind\n");

printf("\n\t Enter the unsigned decimal: ");

scanf_s("%u", &number);

printf("\t Enter the number of bits of rotation: ");

scanf_s("%d", &n);

//Для контроля перевода десятичного числа в шестнадцатеричное

printf("\n\t Hexadecimal numbers: %X\n", number);

NUM1 = dec2hex(number);

strcat_s(str, 127, NUM1);

strcat_s(str, 127, un);

fopen_s(&fid,"temp.txt", "w");

fprintf(fid,"%s", str);

fclose(fid);

fopen_s(&fid,"temp.txt", "r");

fscanf_s(fid,"%x", &uns);

fclose(fid);

printf("\n\t After rotating %d-bits: %X\n", n, rotate(uns, n));

printf("\n\n Press any key: ");

_getch();

return 0; }

//Функция перевода десятичного числа в шестнадцатеричное

char *dec2hex(unsigned int var)

{

int number[64];

int i, j, k = 0;

unsigned int base = 16;

char Hex[128], *PTR;

PTR = Hex;

do {

number[k] = var % base;

++ k;

var /= base;

} while (var != 0);

for (i = k-1, j = 0; i >= 0; --i, ++j) {

Hex[j] = basestr[number[i]];

PTR[j] = Hex[j];

}

PTR[j] = '\0';

return (PTR);

}

//Функция ротации числа влево или вправо

unsigned int rotate (unsigned int var, int n)

{

unsigned int result, bits;

// Ограничение диапазона ротации

if (n > 0)

n = n % 32;

else

n = -(-n % 32);

if (n == 0)

result = var;

else

if (n > 0) // ротация влево

{

bits = var >> (32 - n);

result = var << n | bits;

}

else // ротация вправо

{

n = -n;

bits = var << (32 - n);

result = var >> n | bits;

}

return (result);

}

В программе сначала формируется символьное выражение шестнадцатеричного числа с помощью функции dec2hex(). Затем оно приводится к стандартному виду с помощью функции strcat_s(), которая к имеющемуся значению добавляет содержимое строки от функции dec2hex(). После этого добавляется суффикс u типа unsigned int. Для получения собственно шестнадцатеричного числа выполнена операция записи строки в файл и чтения из файла в шестнадцатеричном формате. Далее используется функция rotate(). Ротация переменной на n битов влево производится в соответствии с алгоритмом, состоящим из трех шагов [2]. Сначала извлекаются левые n бит исходного значения в отдельную переменную (bits), которая сдвигается вправо (с помощью поразрядного сдвига >>) на количество позиций (32n). Затем исходное значение (var) сдвигается влево на n битов, и, наконец, над получившимся значением и извлеченными битами производится операция поразрядной дизъюнкции ИЛИ. Аналогичные действия выполняются при сдвиге вправо.

В озможный результат реализации программы показан на рис.16.6.

Рис.16.6. Результат ротации заданного числа

Задание6

  1. Дополните программу выводом на консоль прочитанного числа из текстового файла.

  2. В качестве вводимых данных используйте год и месяц рождения пользователя (студента).

  3. Выполните ротацию шестнадцатеричных чисел, записанных в текстовый файл с именем compX.txt, где Х – номер компьютера, на котором выполняется лабораторная работа. В качестве тестовых чисел и числа битов ротации примите: 1) 0xABCDEF00u, 8;   2) 0xABCDEF00u,–16; 3) 0xFFFF1122u, 4; 4) 0xFFFF1122u, –2; 5) 0xABCDEF00u, 0; 6) 0xABCDEF00u, 44.

  4. Напишите программу по ротации восьмеричных чисел.

Пример7. Написать программу по решению следующего примера. С клавиатуры вводятся два целых числа. Остатки от деления их на 16 заносятся соответственно в 4 младших и 4 старших разряда одного байта. Затем следует напечатать изображение содержимого сформированного байта [3].

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <locale.h>

// Прототипы функций

void binar (unsigned char);

unsigned char code16 (unsigned int, unsigned int);

int main (void)

{

unsigned int m, n;

unsigned char k;

setlocale(LC_ALL, ".1251");

printf("\n\t Введите первое беззнаковое число N1: ");

scanf_s("%u", &m);

printf("\t Введите второе беззнаковое число N2: ");

scanf_s("%u", &n);

k = code16(m, n);

printf("\n\t Код двух остатков старших и младших разрядов байта: %u", k);

binar(k);

printf("\n Нажмите любую клавишу: ");

_getch();

return 0;

}

// Определение функции

unsigned char code16 (unsigned int a, unsigned 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.

Р ис.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

  1. Выведите на консоль размерность в байтах объединений и структур, определенных в программе.

  2. Проанализируйте результат выполнения программы при изменении размера битовых полей в структуре функции code16().

  3. Видоизмените программу для ввода трех чисел и определения значений разрядных (битовых) полей после занесения в них остатков от деления на целое число 8.

  4. Определите размер в байтах объединения.

  5. Определите размер в байтах вложенной структуры.

Пример8. Используя битовые поля структуры, написать программу вывода на экран дисплея двоичного кода ASCII символа, вводимого с клавиатуры.

Охарактеризуем наборы символов ASCII. Символы сохраняются в памяти компьютеров с помощью числовых кодов. Часто используется кодировка ASCII (american standard code for information interchange – американский стандартный код для обмена информацией). Таблицу символов ASCII можно посмотреть в работе [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.

Рис.16.8. Пример вывода двоичных кодов заданных символов

Задание8

  1. Вместо цикла dowhile примените другой оператор цикла.

  2. Напишите программу вывода всех строчных букв латинского алфавита и их двоичных эквивалентов в кодировке ASCII без ввода их с клавиатуры.

  3. Определите размер в байтах вложенной структуры.

Пример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");

}

}

Вывод двоичного эквивалента сделан побайтно, считая, что в одном байте находится 8 бит.

В озможный результат выполнения программы показан на рис.16.9.

Рис.16.9. Поразрядный сдвиг влево заданного числа

Задание9

  1. Проверьте результат выполнения программы с помощью инженерного калькулятора (calc).

  1. В программу добавьте нумерацию результатов вывода десятичного числа и его двоичного эквивалента.

  2. Введите число своего дня рождения пользователя.

  3. Проверьте программу при вводе отрицательных целых чисел.

  4. Измените программу для поразрядного сдвига вправо.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]