ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 31.03.2021
Просмотров: 232
Скачиваний: 1
Для представления информации, которая содержится в байте состояний, можно использовать следующие битовые поля.
struct status_type {
unsigned delta_cts: 1;
unsigned delta_dsr: 1;
unsigned tr_edge: 1;
unsigned delta_rec: 1;
unsigned cts: 1;
unsigned dsr: 1;
unsigned ring: 1;
unsigned rec_line: 1;
} status;
Чтобы определить, когда можно отправить или получить данные, используется код, подобный следующему.
status = get_port_status();
if(status.cts) cout << "Установка в исходное состояние";
if(status.dsr) cout << "Данные готовы";
Чтобы присвоить битовому полю значение, достаточно использовать такую же форму, которая обычно применяется для элемента структуры любого другого типа. Например, следующая инструкция очищает битовое поле ring:
status.ring = 0;
Как видно из этих примеров, доступ к каждому битовому полю можно получить с помощью оператора "точка". Но если общий доступ к структуре осуществляется через указатель, необходимо использовать оператор "->".
Следует иметь в виду, что совсем необязательно присваивать имя каждому битовому полю. Это позволяет обращаться только к нужным битам, "обходя" остальные. Например, если вас интересуют только биты cts и dsr, вы могли бы объявить структуру status_type следующим образом.
struct status_type {
unsigned : 4;
unsigned cts: 1;
unsigned dsr: 1;
} status;
Обратите здесь внимание на то, что биты после последнего именованного dsr нет необходимости вообще упоминать.
В структуре можно смешивать "обычные" члены с битовыми полями. Вот пример.
struct emp {
struct addr address;
float pay;
unsigned lay_off: 1; // работает или нет
unsigned hourly: 1: // почасовая оплата или оклад
unsigned deductions: 3: // удержание налога
};
Эта структура определяет запись по каждому служащему, в которой используется только один байт для хранения трех элементов информации: статус служащего, характер оплаты его труда (почасовая оплата или твердый оклад) и налоговая ставка. Без использования битовых полей для хранения этой информации пришлось бы занять три байта.
Использование битовых полей имеет определенные ограничения. Программист не может получить адрес битового поля или ссылку на него. Битовые поля нельзя хранить в массивах. Их нельзя объявлять статическими. При переходе от одного компьютера к другому невозможно знать наверняка порядок следования битовых полей: справа налево или слева направо. Это означает, что любая программа, в которой используются битовые поля, может страдать определенной зависимостью от марки компьютера. Возможны и другие ограничения, связанные с особенностями реализации компилятора C++, поэтому имеет смысл прояснить этот вопрос в соответствующей документации.
В следующем разделе представлена программа, в которой используются битовые поля для отображения символьных ASCII-кодов в двоичной системе счисления.
Объединения
Объединение состоит из нескольких переменных, которые разделяют одну и ту же область памяти.
Объединение состоит из нескольких переменных, которые разделяют одну и ту же область памяти. Следовательно, объединение обеспечивает возможность интерпретации одной и той же конфигурации битов двумя (или более) различными способами. Объявление объединения, как нетрудно убедиться на следующем примере, подобно объявлению структуры.
union utype {
short int i;
char ch;
};
Объявление объединения начинается с ключевого слова union.
Здесь объявляется объединение, в котором значение типа short int и значение типа char разделяют одну и ту же область памяти. Необходимо сразу же прояснить один момент: невозможно сделать так, чтобы это объединение хранило и целочисленное значение, и символ одновременно, поскольку переменные i и ch накладываются (в памяти) друг на друга. Но программа в любой момент может обрабатывать информацию, содержащуюся в этом объединении, как целочисленное значение или как символ. Следовательно, объединение обеспечивает два (или больше) способа представления одной и той же порции данных. Как видно из этого примера, объединение объявляется с помощью ключевого слова union.
Как и при использовании структур, при объявлении объединения не определяется ни одна переменная. Переменную можно объявить, разместив ее имя в конце объявления либо воспользовавшись отдельной инструкцией объявления. Чтобы объявить переменную объединения именем u_var типа utype, достаточно записать следующее:
utype u_var;
В переменной объединения u_var как переменная i типа short int, так и символьная переменная ch разделяют одну и ту же область памяти. (Безусловно, переменная i занимает два байта, а символьная переменная ch использует только один.) Как переменные i и ch разделяют одну область памяти, показано на рис. 10.2.
При объявлении объединения компилятор автоматически выделяет область памяти, достаточную для хранения в объединении переменных самого большого по объему типа.
Чтобы получить доступ к элементу объединения, используйте тот же синтаксис, который применяется и для структур: операторы "точка" и "стрелка". При непосредственном обращении к объединению (или посредством ссылки) используется оператор "точка". Если же доступ к переменной объединения осуществляется через указатель, используется оператор "стрелка". Например, чтобы присвоить букву 'А' элементу ch объединения u_var, достаточно использовать такую запись.
u_var.ch = 'А';
В следующем примере функции передается указатель на объединение u_var. В теле этой функции с помощью указателя переменной i присваивается значение 10.
// ...
func1(&u_var); // Передаем функции func1() указатель на объединение u_var.
// ...
}
void fund (utype *un)
{
un->i = 10; /* Присваиваем число 10 члену объединения u_var с помощью указателя. */
}
Поскольку объединения позволяют вашей программе интерпретировать одни и те же данные по-разному, они часто используются в случаях, когда требуется необычное преобразование типов. Например, следующая программа использует объединение для перестановки двух байтов, которые составляют короткое целочисленное значение. Здесь для отображения содержимого целочисленных переменных используется функция disp_binary(), разработанная в главе 9. (Эта программа написана в предположении, что короткие целочисленные значения имеют длину два байта.)
// Использование объединения для перестановки двух байтов в рамках короткого целочисленного значения.
#include <iostream>
using namespace std;
void disp_binary(unsigned u);
union swap_bytes {
short int num;
char ch[2];
};
int main()
{
swap_bytes sb;
char temp;
sb.num = 15; // двоичный код: 0000 0000 0000 1111
cout << "Исходные байты: ";
disp_binary(sb.ch[1]);
cout << " ";
disp_binary(sb.ch[0]);
cout << "\n\n";
// Обмен байтов.
temp = sb.ch[0];
sb.ch[0] = sb.ch[1];
sb.ch[1] = temp;
cout << "Байты после перестановки: ";
disp_binary(sb.ch[1]);
cout << " ";
disp_binary(sb.ch[0]);
cout << "\n\n";
return 0;
}
// Отображение битов, составляющих байт.
void disp_binary(unsigned u)
{
register int t;
for(t=128; t>0; t=t/2)
if(u & t) cout << "1 ";
else cout << "0 ";
}
При выполнении программа генерирует такие результаты.
Исходные байты: 0000 0000 0000 1111
Байты после перестановки: 0000 1111 0000 0000
В этой программе целочисленной переменной sb.num присваивается число 15. Перестановка двух байтов, составляющих это значение, выполняется путем обмена двух символов, которые образуют массив ch. В результате старший и младший байты целочисленной переменной num меняются местами. Эта операция возможна лишь потому, что как переменная num, так и массив ch разделяют одну и ту же область памяти.
В следующей программе демонстрируется еще один пример использования объединения. Здесь объединения связываются с битовыми полями, используемыми для отображения в двоичной системе счисления ASCII-кода, генерируемого при нажатии любой клавиши. Эта программа также демонстрирует альтернативный способ отображения отдельных битов, составляющих байт. Объединение позволяет присвоить значение нажатой клавиши символьной переменной, а битовые поля используются для отображения отдельных битов.
// Отображение ASCII-кода символов в двоичной системе счисления.
#include <iostream>
#include <conio.h>
using namespace std;
// Битовые поля, которые будут расшифрованы.
struct byte {
unsigned a : 1;
unsigned b : 1;
unsigned с : 1;
unsigned d : 1;
unsigned e : 1;
unsigned f : 1;
unsigned g : 1;
unsigned h : 1;
};
union bits {
char ch;
struct byte bit;
}ascii;
void disp_bits(bits b);
int main()
{
do {
cin >> ascii.ch;
cout << ":";
disp_bits(ascii);
}while(ascii.ch!='q'); // Выход при вводе буквы "q".
return 0;
}
// Отображение конфигурации битов для каждого символа.
void disp_bits(bits b)
{
if(b.bit.h) cout << "1";
else cout << "0";
if(b.bit.g) cout << "1";
else cout << "0";
if(b.bit.f) cout << "1";
else cout << "0 ";
if(b.bit.e) cout << "1";
else cout << "0";
if(b.bit.d) cout << "1";
else cout << "0";
if(b.bit.c) cout << "1";
else cout << "0";
if(b.bit.b) cout << "1";
else cout << "0";
if(b.bit.a) cout << "1";
else cout << "0";
cout << "\n";
}
Вот как выглядит один из возможных вариантов выполнения этой программы.
а: 0 1 1 0 0 0 0 1
b: 0 1 1 0 0 0 1 0
с: 0 1 1 0 0 0 1 1
d: 0 1 1 0 0 1 0 0
е: 0 1 1 0 0 1 0 1
f: 0 1 1 0 0 1 1 0
g: 0 1 1 0 0 1 1 1
h: 0 1 1 0 1 0 0 0
i: 0 1 1 0 1 0 0 1
j: 0 1 1 0 1 0 1 0
k: 0 1 1 0 1 0 1 1
1: 0 1 1 0 1 1 0 0
m: 0 1 1 0 1 1 0 1
n: 0 1 1 0 1 1 1 0
o: 0 1 1 0 1 1 1 1
p: 0 1 1 1 0 0 0 0
q: 0 1 1 1 0 0 0 1
Важно! Поскольку объединение предполагает, что несколько переменных разделяют одну и ту же область памяти, это средство предоставляет программисту возможность хранить информацию, которая (в зависимости от ситуации) может содержать различные типы данных, и получать доступ к этой информации. По сути, объединения обеспечивают низкоуровневую поддержку принципов полиморфизма. Другими словами, объединение обеспечивает единый интерфейс для нескольких различных типов данных, воплощая таким образом концепцию "один интерфейс — множество методов" в своей самой простой форме.
Анонимные объединения
Анонимные объединения позволяют объявлять переменные, которые разделяют одну и ту же область памяти.
В C++ предусмотрен специальный тип объединения, который называется анонимным. Анонимное объединение не имеет наименования типа, и поэтому объект такого объединения объявить невозможно. Но анонимное объединение сообщает компилятору о том, что его члены разделяют одну и ту же область памяти. При этом обращение к самим переменным объединения происходит непосредственно, без использования оператора "точка". Рассмотрим такой пример.
//Демонстрация использования анонимного объединения.
#include <iostream>
using namespace std;
int main()
{
// Это анонимное объединение.
union {
short int count;
char ch[2];
};
// Вот как происходит непосредственное обращение к членам анонимного объединения.
ch[0] = 'X';
ch[1] = 'Y';
cout << "Объединение в виде символов: " << ch[0] <<ch[1] << '\n';
cout << "Объединение в виде целого значения: " <<count << '\n';
return 0;
}
Эта программа отображает следующий результат.
Объединение в виде символов: XY
Объединение в виде целого значения: 22872
Число 22872 получено в результате помещения символов X и Y в младший и старший байты переменной count соответственно. Как видите, к обеим переменным, входящим в состав объединения, как count, так и ch, можно получить доступ так же, как к обычным переменным, а не как к составляющим объединения. Несмотря на то что они объявлены как часть анонимного объединения, их имена находятся на том же уровне области видимости, что и другие локальные переменные, объявленные на уровне объединения. Таким образом, член анонимного объединения не может иметь имя, совпадающее с именем любой другой переменной, объявленной в той же области видимости.
Анонимное объединение представляет собой средство, с помощью которого программист может сообщить компилятору о своем намерении, чтобы две (или больше) переменные разделяли одну и ту же область памяти. За исключением этого момента, члены анонимного объединения ведут себя подобно любым другим переменным.
Использование оператора sizeof для гарантии переносимости программного кода
Как было показано, структуры и объединения создают объекты различных размеров, которые зависят от размеров и количества их членов. Более того, размеры таких встроенных типов, как int, могут изменяться при переходе от одного компьютера к другому. Иногда компилятор заполняет структуру или объединение так, чтобы выровнять их по границе четного слова или абзаца. (Абзац содержит 16 байт.) Поэтому, если в программе нужно определить размер (в байтах) структуры или объединения, используйте оператор sizeof. Не пытайтесь вручную выполнять сложение отдельных членов. Из-за заполнения или иных аппаратно-зависимых факторов размер структуры или объединения может оказаться больше суммы размеров отдельных их членов.
И еще. Объединение всегда будет занимать область памяти, достаточную для хранения его самого большого члена. Рассмотрим пример.
union х {
char ch;
int i;
double f;
} u_var;
Здесь при выполнении оператора sizeof u_var получим результат 8 (при условии, что double-значение занимает 8 байт). Во время выполнения программы не имеет значения, что реально будет храниться в переменной u_var; здесь важен размер самой большой переменной, входящей в состав объединения, поскольку объединение должно иметь размер самого большого его элемента.
Переходим к объектно-ориентированному программированию
Эта глава заключает описание не объектно-ориентированных атрибутов C++. Начиная со следующей главы, мы будем рассматривать средства, которые поддерживают объектно-ориентированное программирование (Object Oriented Programming— OOP), или ООП. Чтобы понять объектно-ориентированные средства C++ и научиться их эффективно применять, необходимо глубокое понимание материала этой и предыдущих девяти глав. Поэтому, возможно, вам стоит повторить пройденный материал. Особое внимание при повторении уделите указателям, структурам, функциям и перегрузке функций.