Попробуйте часть кода до слова main заменить на #include "stdafx.h" //1 #include <iostream> //2 using namespace std; //3 |
Пользовательские типы данных
9.2. Перечисления
Перечисления представляют собой список идентификаторов, введенных пользователем:
enum name_list {name1,name2,...};
За каждым таким именем по умолчанию закрепляются целочисленные константы:
- имени name1 соответствует константа 0;
- имени name2 соответствует константа 1;
- .....................................
В чем смысл замены целочисленных констант такими символьными обозначениями? Дело в том, что в конкретных прикладных задачах удобно иметь дело с мнемоническими обозначениями характеристик некоторых объектов. Например, имея дело с цветами радуги, было бы удобно ввести обозначение для палитры радуги ( rainbow ) и перечислить в ней цвета в том порядке, как они упорядочены в природе (" каждый охотник желает знать, где сидят фазаны " – красный, оранжевый, желтый, зеленый, голубой, синий, фиолетовый):
enum rainbow {red, orange, yellow, green, aqua, blue, purple};
Переменная r_col, которая будет в дальнейшем обозначать цвет радуги, должна быть связана с именем списка, и это позволит присваивать ей значения, более понятные, чем соответствующие целые числа:
enum rainbow r_col; r_col=yellow; ............... if(r_col==yellow)...
В языке C++ при объявлении переменных типа перечисление разрешается опускать служебное слово enum:
rainbow r_col=yellow;
В программах обработки экономической информации очень часто приходится иметь дело с названиями месяцев, дней недели. В таких случаях полезно прибегать к перечислениям типа:
enum week {sunday=1, monday, tuesday, wednesday, thursday, friday, saturday}; enum month {jan=1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec };
В приведенных выше примерах две особенности. Во-первых, нумерация констант по умолчанию с 0 противоречит общепринятым нормам работы с календарем. Поэтому для первой константы указано нестандартное значение 1, а все последующие будут пронумерованы в порядке возрастания номеров. Во-вторых, в перечислении названий месяцев имеется возможность нарваться на сообщение об ошибке. Дело в том, что сокращения для " октября " и " декабря " совпадают со служебными словами oct - восьмеричный и dec - десятичный, которые участвуют в управлении потоковым выводом числовых данных. Можно перейти к другим обозначениям, где первая буква месяца будет заглавной, тогда конфликта удастся избежать.
Перечисления очень широко используются многими системными программами, особенно графическими:
enum line_style{SOLID_LINE, //сплошная линия DOTTED_LINE, //пунктирная линия CENTER_LINE, //штрих-пунктирная линия DASHED_LINE, //штриховая линия USERBIT_LINE}; //линия, определяемая пользователем enum COLORS {BLACK,BLUE,GREEN,CYAN,RED,MAGENTA,BROWN,...};
Системный набор операций над переменными типа перечислений довольно ограниченный: им можно присваивать значения из объявленного списка, сравнивать значения однотипных переменных, передавать в качестве параметров другим функциям. Попытка вывести их значения приводит к появлению на экране приписанных им числовых номеров. Даже какой-то контроль за выходом из допустимого числового интервала для этих данных не реализован. Об этом свидетельствует следующий пример:
#include <stdio.h> #include <conio.h> void main() { enum qq {zero, one, two, three}; enum qq a,b; a=one; b=a+two; printf("a=%d b=%d",a,b); getch(); } //=== Результат работы === a=1 b=3
Правда, при компиляции 7-й строки было выдано предупреждение о попытке присвоения значения типа int переменной типа qq, но все обошлось. Однако замена на оператор b=a+three; никаких эмоций у компилятора не вызвала и был получен результат b=4. Даже попытка выполнить b=5 ; тоже прошла гладко. Так что с данными типа enum компилятор работает как с целыми числами и на выход за пределы заданного диапазона внимания не обращает.
Обратим внимание еще на один нюанс в приписывании числовых номеров константам из перечисления:
enum num12 {one=1,ein=1,two,zwei=2};
Этот пример демонстрирует, что числовые номера в списке констант могут дублироваться, но сами имена должны быть уникальными. Использование одного и того же имени в двух разных перечислениях недопустимо.
9.3. Объединения
Объединения – это такие наборы данных, которые компилятор должен разместить в оперативной памяти, начиная с одного и того же места. Впервые такое совмещение разных данных появилось в языке ФОРТРАН, где для этой цели использовался оператор EQUIVALENCE (эквивалентность). Основным назначением этого оператора была попытка экономии оперативной памяти за счет размещения вновь используемых массивов на месте уже отработавших массивов. В последующем этот оператор использовался и для совместного доступа к одним и тем же полям оперативной памяти как к данным разного типа. Наконец, еще одна дополнительная услуга со стороны оператора EQUIVALENCE состояла в том, что программисты, создающие разные фрагменты программы могли использовать разные имена для обозначения одних и тех же физических величин. Эквивалентность двух разных имен позволяла свести к минимуму переделки при объединении фрагментов программ.
В языках C, C++ объединения создаются с помощью оператора union. В рамках MS-DOS, где нехватка оперативной памяти давала себя знать, с помощью объединений можно наложить друг на друга массивы, используемые в разное время. Однако наиболее важная цель объединений – расположить в одном и том же месте данные разного типа. Это позволяет обращаться к тем или иным полям, используя переменные разного типа. Наиболее характерным объектом такого типа является ячейка электронной таблицы, в которой пользователь может разместить текст или числовое выражение того или иного типа.
Самым употребительным объединением в рамках MS-DOS было использование машинных регистров при обращении к функциям BIOS и операционной системы. Для этой цели в заголовочном файле dos.h было определено объединение REGS:
struct WORDREGS // структура из 16-битных данных { unsigned int ax,bx,cx,dx,si,di,cflag,flags; }; struct BYTEREGS // структура из 8-битных данных { unsigned char al,ah,bl,bh,cl,ch,dl,dh; } union REGS {struct WORDREGS x; struct BYTEREGS h; };
В этих объявлениях содержатся описания двух структур, которые имитируют распределение в оперативной памяти машинных регистров процессора Intel-8086. На языке ассемблера эти регистры обозначаются как 16-битные регистры общего назначения (AX, BX, CX, DX), индексные регистры (SI, DI) и регистры флагов (CFLAG, FLAGS). Особенность регистров общего назначения в том, что каждый из них объединяет по 2 байта, к которым возможен автономный доступ – отдельно к старшему байту регистра (AH, BH, CH, DH), отдельно к младшему байту (AL, BL, CL, DL). При обращениях к функциям MS-DOS приходится оперировать и с каждым байтом того или иного регистра, и с общим содержимым обоих байтов. Например, функция перевода курсора дисплея в заданную позицию, реализуется следующим фрагментом программы на языке ассемблера:
MOV AH,2 ; номер функции 2 засылается в регистр AH MOV BH,0 ; номер страницы в текстовом режиме MOV DH,10 ; номер строки, в которую переводится курсор MOV DL,25 ; номер колонки INT 10H ; вызов прерывания с номером 16
Для того чтобы выполнить аналогичные действия (не прибегая к библиотечной функции gotoxy ) на языке C надо проделать следующие операции:
union REGS r; //заводим область регистров в памяти ............... r.h.ah=2; //засылаем номер функции в "регистр" AH r.h.bh=0; //засылаем номер страницы в "регистр" BH r.h.dh=y; //засылаем номер строки в "регистр" DH r.h.dl=x; //засылаем номер столбца в "регистр" DL int86(0x10,&r,&r); //имитация прерывания с номером 10h
Функция int86 перепишет содержимое объединения r в машинные регистры, предварительно сохранив их содержимое, выполнит команду прерывания, передающую управление подфункции MS-DOS с номером 2, которая переместит курсор в заданную позицию. По окончании работы подфункции содержимое машинных регистров запомнится в объединении r, а их прежнее содержимое будет восстановлено.
Может быть, вам покажется, что приведенный фрагмент излишне усложнен, но реальная работа функции gotoxy(x,y) требует еще большего числа операций.
В языке C++ также была предпринята попытка использовать объединения для создания классов. Однако о деталях другого использования объединений в разделе "C++ и объектно-ориентированное программирование".