Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 25.11.2008 | Доступ: свободный | Студентов: 9593 / 1296 | Оценка: 4.06 / 3.66 | Длительность: 21:16:00
Лекция 10:

Пользовательские типы данных

< Лекция 9 || Лекция 10: 123 || Лекция 11 >

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++ и объектно-ориентированное программирование".

< Лекция 9 || Лекция 10: 123 || Лекция 11 >
Alexey Ku
Alexey Ku

Попробуйте часть кода до слова main заменить на 

#include "stdafx.h" //1

#include <iostream> //2
#include <conio.h>

using namespace std; //3

Александр Талеев
Александр Талеев

#include <iostream.h>
#include <conio.h>
int main(void)
{
int a,b,max;
cout << "a=5";
cin >> a;
cout <<"b=3";
cin >> b;
if(a>b) max=a;
else max=b;
cout <<" max="<<max;
getch();
return 0;
}

при запуске в visual express выдает ошибки 

Ошибка    1    error C1083: Не удается открыть файл включение: iostream.h: No such file or directory    c:\users\саня\documents\visual studio 2012\projects\проект3\проект3\исходный код.cpp    1    1    Проект3

    2    IntelliSense: не удается открыть источник файл "iostream.h"    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    1    1    Проект3

    3    IntelliSense: идентификатор "cout" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    6    1    Проект3

    4    IntelliSense: идентификатор "cin" не определен    c:\Users\Саня\Documents\Visual Studio 2012\Projects\Проект3\Проект3\Исходный код.cpp    7    1    Проект3

при создании файла я выбрал пустой проект. Может нужно было выбрать консольное приложение?