Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.docx
Скачиваний:
31
Добавлен:
11.02.2015
Размер:
6.34 Mб
Скачать

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

  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;

} if (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: %c\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,"%c", 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;

} if (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(americanstandardcodeforinformationinterchange– американский стандартный код для обмена информацией). Таблицу символов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);

} if ((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. Вместо цикла doifпримените другой оператор цикла.

  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("%c", &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 %c:\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 %c:\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. Измените программу для поразрядного сдвига вправо.

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