В этой главе рассматриваются некоторые дополнительные типы данных, используемые в С и C++: структуры, объединения, битовые поля и ряд других. Структуры являются очень важным средством, так как служат основой для построения классов — фундаментального понятия объектно-ориентированного программирования. Разобравшись в принципах создания структур, вы легко поймете, как работают классы в C++. Поэтому начнем мы главу с того, что выясним, как формировать простые структуры и массивы структур, передавать их в функции и получать доступ к элементам структур посредством указателей. Объединения и битовые поля являются близкими к структурам типами данных, и после знакомства со структурами вам будет несложно понять их особенности.
Структуры
Понятию структуры можно легко найти аналог в повседневной жизни. Обычная записная книжка, содержащая адреса друзей, телефоны, дни рождений и прочую информацию, по сути своей является структурой взаимосвязанных элементов. Список файлов и папок в окне Windows — тоже структура. Точнее сказать, это все примеры использования структур, но что такое структура сама по себе? Можно дать следующее определение: структура — это группа переменных разных типов, объединенных в единое целое.
Структура создается с помощью ключевого слова struct, за которым следует необязательный тег, а затем — список членов структуры. Тег становится именем нового типа данных и может использоваться для создания переменных этого типа. Описание структуры в общем виде выглядит следующим образом:
struct тег { тип1 имя1;
тип2 имя2;
типЗ имяЗ;
.
.
.
тип-n имя-п; };
В примерах программ этой главы мы будем работать с такой структурой:
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
int iyear;
long lmotor_hours;
float fsaleprice; };
В данном случае создается структура с именем stboat, содержащая описание моторной лодки. Массив szmodel хранит название модели лодки, а массив szserial — ее регистрационный номер. В переменной iyear записан год изготовления лодки, в переменной lmotor_hours— наработанный ресурс мотора в часах, в переменной fsaleprice — цена.
Создать переменную на основании описанной структуры можно следующим образом:
struct stboat stused_boat;
В этом выражении объявляется переменная stused_boat типа structstboat. Допускается и такое объявление:
struct stboat {
char szmodel[1STRING15 + iNULL_CHAR];
char szserial[iSTRING20 + 1NULL_CHAR];
int iyear;
long lmotor_hours;
float fsaleprice; } stused_boat;
В этом случае переменная создается одновременно с описанием структуры. Если больше не предполагается создавать переменные данного типа, то тег структуры можно не указывать:
struct {
char szmodel[iSTRINGIS + iNULL CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
int iyear;
long lmotor_hours;
float fsaleprice;
} stused_boat;
Созданная структура называется безымянной. Больше нигде в программе нельзя будет создать ее экземпляры, разве только придется полностью повторить ее описание. Но зато во время описания структуры можно объявить сразу несколько переменных:
struct {
char szmodel[1STRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
int iyear;
long lmotor_hours;
float fsaleprice; } stboatl, stboat2, stboat3;
Компилятор зарезервирует для членов структуры необходимый объем памяти, точно так же, как при объявлении обычных переменных.
Дополнительный синтаксис структур в C++
В C++ при создании экземпляра структуры, описанной ранее, ключевое слово structможно не указывать:
/* действительно как в С, так и в
C++ */
struct stboat stused_boat;
// действительно только в C++
stboat stused_boat;
Доступ к членам структур
Доступ к отдельному члену структуры можно получить с помощью оператора точки:
имя__переменной. член_ структуры
Например, в языке С запросить содержимое поля szmodel описанной выше структуры stused_boat можно с помощью следующего выражения:
gets(stused_boat.szmodel);
Вывести полученное значение на экран можно так:
printf("%s",stused_boat.szmodel);
В C++ применяется аналогичный синтаксис:
cin >> stused_boat.szmodel; cout << stused boat.szmodel;
В следующем примере используется структура stboat, описание которой мы рассматривали выше.
/*
* struct, с
* Эта программа на языке С демонстрирует работу со структурой.
*/
#include <stdio.h>
#define iSTRINGIS 15
#define 1STRING20 20 #define iNULL_CHAR 1
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
int iyear;
long lmotor_hours;
float fsaleprice; ) stused_boat;
int main(void)
*{
printf("\nВведите модель судна: "); gets(stused_boat.szmodel) ;
printf ("\nВведите регистрационный номер судна: ")/' gets(stused_boat.szserial) ;
printf("\nВведите год изготовления судна: "); scanf("%d",&stused_boat.iyear) ;
printf("\nВведите число моточасов, наработанных двигателем: "); scanf("%d",&stused_boat.lmotor_hours) ;
printf("\nВведите стоимость судна: "); scanf("%a",Sstused_boat.fsaleprice);
printf("\n\n");
printf("Судно %s %dгода выпуска с регистрационным номером #%s,\n",
stused_boat.szmodel, stused_boat.iyear,
stused_boat.szserial);
printf("отработавшее%d моточасов,",
stused_boat.lmotor_hours);
printf(" былопроданоза$%8.2f.\n", stused_boat.fsaleprice);
return(0);
}
При выполнении этой программы на экран будет выведена информация примерно следующего содержания:
Судно "Чайка" 1982 года выпуска с регистрационным номером #XA1011, отработавшее 34187 моточасов, было продано за $18132.00.
Передача структур в качестве аргументов функции
Часто бывает необходимо передать структуру в функцию. При этом аргумент-структура передается по значению, то есть в функции модифицируются лишь копии исходных данных. В прототипе функции следует задать структуру в качестве параметра (в C++ указывать ключевое слово struct необязательно):
/* синтаксис объявления функции в
С и C++ */
voidvprint_data (structstboatstused_boat) ;
// синтаксис, доступный только в
C++
voidvprint_data (stboatstused_boat) ;
Следующая программа является модифицированной версией предыдущего примера. В ней структура stused_boatпередается в функцию vprint_data( ) .
/*
* structfn.c
* Эта программа на языке С демонстрирует передачу структуры в функцию.
*/
#include <stdio.h>
#define iSTRINGIS 15
#define iSTRING20 20
#define iNULL_CHAR 1
struct stboat (
char szmodel[iSTRING15 + iNULL_CHAR] ;
char szserial[iSTRING20 + iNULL_CHAR] ;
int iyear;
long lmotor_hours;
float fsaleprice;
};
void vprint_data(struct stboat stused_boat);
int main(void) {
struct stboat stused_boat;
printf("\nВведите модель судна: "); gets(stused_boat.szmodel); \
printf("\nВведите регистрационный номер судна: "); gets(stused_boat.szserial) ;
printf("\nВведите год изготовления судна: "); scanf("%d",&stused_boat.iyear);
printf("\nВведите число моточасов, наработанных двигателем: "); scanf("%d",&stused_boat.lmotor_hours);
printf("\nВведите стоимость судна: "); scanf("%f",&stused_boat.fsaleprice) ;
vprint_data(stused_boat); return(0);
void vprint_data(struct stboat stused_boat) {
printf("\n\n");
printf("Судно %s %d года
выпуска с регистрационным номером#%s,\n",
stused_boat.szmodel, stused_boat.iyear,
stused_boat.szserial);
printf("отработавшее%d моточасов,", stused_boat.lmotor_hours);
printf(" было продано за$%8.2f\n", stused_boat.fsaleprice);
}
Массивы структур
В следующей программе создается массив структур с информацией о лодках.
/*
* structar.c
* Эта программа на языке С демонстрирует работу с массивом структур.
*/
#include <stdio.h>
#define iSTRINGIS 15
#define iSTRING20 20
#define iNULL_CHAR 1
#define iMAX_BOATS 50
struct stboat {
char szmodel[iSTRINGIS + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
char szcomment[80];
int iyear;
long lmotor_hours;
float fsaleprice;
};
int main (void)
{
int i, iinstock;
struct stboat astBoats [ iMAX_BOATS ];
printf( "Информацию о скольких лодках следует ввести в базу 'данных? ") ; scanf("%d",&iinstock) ;
for (i =0;i < iinstock; i++) (
_flushall();
/* очистка буфера */
printf ("\nВведите модель судна: ") ; gets (astBoats [i]. szmodel)
;
printf ("\nВведите регистрационный номер судна: "); gets (astBoats [i]. szserial) ;
printf ("\nВведите строку заметок о судне: "); gets (astBoats [i]. szcomment) ;
printf ("\nВведите год изготовления судна: "); scanf("%d",SastBoats [i].iyear) ;
printf ("\nВведите число моточасов, наработанных двигателем: "); scanf ("%d",SastBoats [i] . lmotor_hours) ;
printf ("\nВведите стоимость судна: "); scanf ("%f",SastBoatsfi] .fsaleprice) ;
}
printf("\n\n");
for(i=0;i < linstock; i++) {
printf("Судно%s %d года выпуска
с регистрационным номером#%s,\n"),
astBoats[i].szmodel, astBoats[i].iyear, astBoats[i].szserial);
printf("отработавшее%d моточасов.\n"),
astBoafcs [i].lmotor_hours); printf("%s\n",astBoats[i].szcomment); printf ("ВСЕГО$%8.2f\n\n",astBoats[i].fsaleprice); }
return(0); }
В этой программе мы добавили в структуру stboatновую переменную — массив szcomment[80], содержащий строку с краткой характеристикой судна.
Использование функции _flushall() внутри первого цикла for необходимо, чтобы удалить из входного потока символ новой строки, оставшийся после выполнения предшествующей функции scanf() (либо той, что стоит перед циклом, либо той, что стоит в конце цикла). Вспомните, что функция gets() считывает все символы вплоть до \n. В паре с функцией scanf(), которая оставляет в потоке символ новой строки, следующая за ней функция gets() прочитает пустую строку. Чтобы предотвратить подобный нежелательный ход событий, вызывается функция flushall (), помимо прочего, очищающая буферы всех открытых входных потоков.
Вот как примерно будут выглядеть результаты работы программы:
Судно "Чайка" 1982 гола выпуска с регистрационным номером #XA1011,отработавшее 34187 моточасов.
Великолепное судно для морских прогулок.
ВСЕГО $18132.00
Судно "Метеорит" 1988 года выпуска с регистрационным номером #KИ8096, отработавшее 27657 моточасов.
Отлично выглядит, прекрасные ходовые
качества.
ВСЕГО $20533.00
Судно "Спрут" 1993года выпуска с регистрационным номером #ДД7845, отработавшее 1000 моточасов. Надежно и экономично. ВСЕГО $36234.00
Указатели на структуры
Следующая программа является вариантом предыдущего примера, в котором для получения доступа к отдельным членам структуры применяются указатель и оператор ->.
/*
* structpt.c
* Эта программа на языке С демонстрирует применение оператора ->.
*/
#include <stdio.h>
#define iSTRINGIS 15
#define 1STRING20 20
#define iNULL_CHAR 1
#define iMAX_BOATS 50
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR] ;
char szcomment[80];
int iyear;
long lmotor_hours;
float fsaleprice; } ;
int main(void)
int i, iinstock;
struct stboat astBoats[iMAX_BOATS], *pastBoats;
pastBoats= sastBoats[0];
printf("Информацию о скольких лодках следует ввести в базу данных? ");
scanf("%d",Siinstock);
for (i =0;i < linstock; i++) {
_flushall(); /* очисткабуфер
*/
printf("\nВведите модель судна: "); gets (pastBoats->szmodel)
;
printf("\nВведите регистрационный номер судна: "); gets(pastBoats->szserial) ;
printf("\nВведите строку заметок
о судне: ");
gets(pastBoats->szcomment);,
printf("\nВведите год изготовления судна: "); scanf("%d",SpastBoats->iyear) ;
printf("\nВведите число моточасов, наработанных двигателем: "); scanf("%d",SpastBoats->lmotor_hours);
printf("\nВведите стоимость судна: "); scanf("%f",&pastBoats->fsaleprice) ;
pastBoats++;
}
pastBoats = SastBoats [0]; printf ("\n\n");
for (d = 0; i < linstock; i++) {
printf ("Судно%s %d года выпуска
с регистрационным номером#%s,\n",
pastBoats->szmodel, pastBoats->iyear, pastBoats->szserial) ;
printf ("отработавшее%d моточасов . \n",pastBoats->lmotor_hours)
;
printf ("%s\n",pastBoats->szcomment) ;
printf ("ВСЕГО$%8.2f .\n\n",pastBoats->fsaleprice) ;
pastBoats++;
}
return(0);
}
К отдельному члену структуры, представленной указателем, можно обратиться и таким способом:
gets ( (*pastBoats) .szmodel) ;
Дополнительная пара скобок необходима, поскольку оператор прямого доступа к члену структуры (.) имеет больший приоритет, чем оператор раскрытия указателя (*). Использование оператора косвенного доступа (->) делает запись проще и аккуратнее:
gets (pastBoats->szmodel) ;
Передача массива структур в качестве аргумента функции
Ранее в этой главе уже говорилось о том, что структуры передаются в функции по значению. Если же вместо самой структуры передавать указатель на нее, то это может существенно ускорить выполнение программы.
/*
* structaf.с
* В этой программе на языке С в функцию передается указатель на структуру.
*/
#include <stdio.h>
#define i STRING 15 15
#define 1STRING20 20
#define iNULL_CHAR 1
#define iMAX_BOATS 50
int linstock;
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR] ;
char szserial[iSTRING20 + iNULL_CHAR] ;
char szcomment [80];
int iyear;
long lmotor_hours;
float fsaleprice;
};
void vprint_data(struct stboat *stused_boatptr);
int main(void) {
int i;
struct stboat astBoats[iMAX_BOATS], *pastBoats;
pastBoats= SastBoats[0];
printf("Информацию о скольких лодках следует ввести в базу данных? "); scanf("%d",Siinstock);
for(i= 0; i < iinstock; i++) {
_flushall(); /* очищаетбуфер */ printf("\nВведитемодельсудна: "); gets(pastBoats->szmodel);
printf("\nВведите регистрационный номер судна: "); gets(pastBoats->szserial);
printf("\nВведите строку заметок о судне: "); gets (pastBoats->szcoiranent) ;
printf("\nВведите год изготовления судна: ");
scanf("%d",&pastBoats->iyear);
printf("\nВведите число моточасoв, наработанных двигателем: "); scanf("%d"/&pastBoats->lmotor_hours);
printf("\nВведите стоимость судна: ");
scanf("%f",spastBoats->fsaleprice);
pastBoats++;
)
pastBoats = SastBoats[0]; vprint_data(pastBoats);
return(0); }
void vprint_data (struct stboat *stused_boatptr) {
int i;
printf ("\n\n");
for(i=0;i < linstock; i++){
printf ("Судно%s %d года выпуска с регистрационным номером#%s,\n", stused_boatptr->szmodel, stused_boatptr->iyear, stused_boatptr->szserial) ;
printf("отработавшее %dмоточасов.
\n",stused_boatptr->lmotor_hours) ,
printf ("%s\n",stused_boatptr->szcomment) ;
printf ("ВСЕГО$%8.2f\n\n",stused_boatptr->fsaleprice) ;
stused_boatptr++;
}
Структуры в C++
Ниже показана программа на C++, являющаяся аналогом рассмотренного в предыдущем параграфе примера на языке С. В обоих случаях для работы со структурой применяется одинаковый синтаксис.
//
// struct. срр
// В этой программе на языке C++ в функцию передается
// указатель на структуру.
//
#include <iostream.h>
#define 1STRING15 15
#define 1STRING20 20
#define iNULL_CHAR 1
#define iMAX BOATS 50
int linstock;
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
char szcomment[80];
int iyear;
long lmotor_hours;
float fsaleprice; );
void vprint_data (stboat *stused_boatptr) ;
int main (void) {
char newline;
stboat astBoats [iMAX_BOATS] , *pastBoats;
pastBoats = SastBoats [0];
cout<< "Информацию о скольких лодках следует ввести в базу данных?
cin >> linstock;
ford = 0; i < linstock; i++) {
cout << "\nВведите модель судна: "; cin >> pastBoats->szmodel;
cout<< "\nВведите регистрационный номер судна: "; cin>> pastBoats->szserial;
cout<< "\nВведите год изготовления судна: "; ''" " cin>> pastBoats->iyear;
cout<< "\nВведите число моточасов, наработанных двигателем: "; cin >> pastBoats->lmotor_hqurs;
cout<< "\nВведите стоимость судна: ";
cin >> pastBoats->fsaleprice;
cout<< "\nВведите строку заметок о судне: ";
cin.get (newline) ; // пропуск символа новой строки
cin. get (pastBoats->szcomment, 80, '.');
cin. get (newline) ; // пропуск символа новой строки
pastBoats++;
}
pastBoats = sastBoats[0]; vprint_data(pastBoats) ;
return(0); }
void vprint_data(stboat *stused_boatptr) {
int i ;
cout << "\n\n";
for(i=0;i < linstock; i++) {
cout << "Судно " << stused_boatptr->szmodel << " " << stused_boatptr->iyear << " года выпуска с регистрационным номером " << stused_boatptr->szserial << ",\n" << "отработавшее " << stused_boatptr->lmotor_hours << " моточасов.\n";
cout << stused_boatptr->szcomment << ".\n";
cout << "ВСЕГО $" << stused_boatptr->fsaleprice << "\n\n";
Считывание строки заметок о судне реализуется несколько иначе, чем в случае других членов структуры. Вспомните, что оператор >> читает строку символов до первого пробела. Поэтому он не подходит для считывания строки комментариев, состоящей из слов, разделенных пробелами. Для получения всей строки применяется следующая конструкция:
cout<< "\nВведите строку
заметок о судне: ";
cin.get (newline) ; // пропуск символа новой строки
cin.get (pastBoats->szcomment, 80, ' . ' ) ;
cin.get (newline) ; // пропуск символа новой строки
Первая функция cin.get(newline) выполняет то же действие, что и функция _£ lushall( ) в одной из предыдущих программ на языке С. В системах, где данные, вводимые с клавиатуры, буферизируются, часто бывает необходимо удалять из буфера символ новой строки. Существует несколько способов сделать это, но мы показали наиболее лаконичный. Прочитанный символ сохраняется в переменной newline, которая больше нигде в программе не используется.
В третьей строке функция cin . get ( ) считывает максимум 80 символов, причем признаком конца строки задана точка. Таким образом, функция завершается, когда длина строки превышает 79 символов (80-я позиция резервируется для символа \0) или если была введена точка. Сам символ точки не будет прочитан. Поэтому, чтобы строка имела завершенный вид, при выводе на печать точку нужно добавить.
Вложенные структуры
Структуры могут быть вложены друг в друга. То есть некоторая структура может содержать переменные, которые, в свою очередь, являются структурами. Предположим, описана такая структура:
structstowner { /* информация о владельце
судна */
charcname[50];/* имя и фамилия */
intiage; /* возраст */
int isailing_experience;
/* опыт мореплавания */ };
В следующем примере структура stowner становится вложенной в рассмотренную ранее структуру stboat:
struct stboat {
char szmodel[iSTRING15 + iNULL_CHAR];
char szserial[iSTRING20 + iNULL_CHAR];
char szcomment[80]; . struct stowner stowner_record;
int iyear;
long lmotor_hours;
float fsaleprice; } astBoats[iMAX_BOATS];
Чтобы, например, вывести на экран возраст владельца судна, необходимо воспользоваться таким выражением:
printf("%d\n", astBoats[0].stowner_record.iage);
//
В C/C++ существует весьма удобная возможность — "запаковать" набор битовых флагов в единую структуру, называемую битовым полем и расположенную в системном слове. Предположим, например, что в программе, осуществляющей чтение данных с клавиатуры, используется группа флагов, отображающих состояние специальных клавиш, таких как [CapsLock], [NumLock] и т.п. Эти флаги можно организовать так:
struct stkeybits {
unsigned char
rshift 1, /* нажата правая клавиша [Shift] */
lshift 1, /* нажата левая клавиша [Shift] */
Ctrl 1, /* нажата клавиша [Ctrl]*/
alt 1, /* нажата клавиша[Alt] */
scroll 1, /* включен режим[Scroll Lock] */
numlock 1, /* включен режим [NumLock] */
capslock 1, I* включен режим[Caps Lock] */
insert 1, /* включен режим [Insert] */
};
После двоеточия указано число битов, занимаемых флагом. Их может быть более одного. Можно не указывать имя флага, а лишь двоеточие и ширину битовой ячейки. Такие безымянные ячейки предназначены для пропуска нужного количества битов в пределах поля. Если указана ширина 0, следующий флаг будет выровнен по границе типа данных. Разрешается создавать битовые поля только целого типа (char, short, int, long), причем желательно явно указывать модификатор unsigned, чтобы поле не интерпретировалось как знаковое число.
При работе с битовыми полями применяется синтаксис структур. Например, чтобы установить флаг capslock, следует воспользоваться таким выражением:
struct stkeybits flags; flags.capslock = 1;
К битовым полям нельзя применять оператор взятия адреса (&).
Объединения
Объединением называется специального рода переменная, которая в разные моменты времени может содержать данные разного типа. Одно и то же объединение может в одних операциях участвовать как переменная типа int, а в других — как переменная типа float или double. Для всех членов такой переменной резервируется единая область памяти, которую они используют совместно. Размерность объединения определяется размерностью самого "широкого" из указанных в нем типов данных.
Синтаксис
Объединение создается с помощью ключевого слова union:
union тег {
тип1имя1;
тип2 имя2;
типЗ имяЗ;
.
.
.
тип-п имя_п; };
Обратите внимание на схожесть описания структуры и объединения:
union unmany_types {
char с;
int ivalue;
float fvalue;
double dvalue; } my_union;
Необязательный тег играет ту же роль, что и тег структуры: он становится
именем
нового типа данных. Также по аналогии со структурами к члену объединения можно
обратиться с помощью оператора точки (.):
имя_переменной.член_объединения
Создание простейшего объединения
Проиллюстрируем работу с объединением на следующем, довольно простом примере:
//
// union.срр
// Эта программа на языке C++ демонстрирует, как работать с объединением.
//
#include <iostream.h>
union unmany_types {
char с;
int ivalue;
float fvalue;
double dvalue; } my_union;
int main (void) {
// корректные операции ввода/вывода
my_union.c = 'b';
cout << my_union.c << endl;
my_union. ivalue = 1990;
cout << my_union. ivalue << endl;
my_union. fvalue =19.90; , cout << my_union. fvalue << endl;
my_union. dvalue = 987654 . 32E+13; cout << my_union. dvalue << endl;
// некорректные операции ввода/вывода
cout << my_union.c << endl; cout << my_union. ivalue << endl; cout << my_union. fvalue << endl; cout << my_union. dvalue << endl;
// вычисление размера объединения
cout<< "Размер объединения составляет " << sizeof (unmany_types)
<< " байт.";
return(0);
}
Первая часть программы выполняется без ошибок, поскольку данные разных типов присваиваются объединению не одновременно, а последовательно. Во второй части правильно отобразится лишь значение типа double, поскольку оно было занесено последним. Вот что будет выведено на экран:
b
1990
19.9
9.87654е+018
Ш
-154494568
-2.05461е+018
9.87654е+018
Размер объединения составляет: 8 байт.
Ключевое слово typedef
С помощью ключевого слова typedef можно создавать новые типы данных на основании уже существующих. Как правило, это необходимо для упрощения текста программы. Например, выражение
typedef struct stboat* stboatptr;
делает имя stboatptr синонимом конструкции stboat*. Теперь создать указатель на структуру stboat можно будет следующим образом:
stboatptr my_boat;
Перечисления
Перечисления, создаваемые с помощью ключевого слова enum, также служат цели сделать программный код более удобочитаемым. Они представляют собой множества именованных целочисленных констант. Для описания перечисления используется следующий синтаксис:
enum необязательный_тег (константа1
[= значение!],
.
.
.
,
константа-n[= значение-п]};
Как вы уже догадались, необязательный тег служит тем же целям, что и теги структур и объединений. Если тег не указан, то сразу после закрывающей фигурной скобки должен находиться список имен переменных. При наличии тега переменные данного перечисления можно будет создавать в любом месте программы (в C++ в этом случае нет необходимости повторно указывать ключевое слово enum).
Константы, входящие в перечисление, имеют тип int. По умолчанию первая константа равна 0, следующая — 1, потом — 2 и т.д. в арифметической прогрессии. Таким образом, значения констант можно не указывать: они будут вычислены автоматически путем формирования прогрессии с шагом 1 от последнего специфицированного значения. В то же время, для каждой константы после знака равенства можно указывать собственное значение.
Все константы одного перечисления должны иметь разные имена, но их значения могут совпадать. Эти имена должны также отличаться от имен обычных переменных в той же области видимости.
Например, в следующем фрагменте программы можно организовать
цикл от 0 до 4, а можно — от понедельника до пятницы:
enum eweekdays { /* будние дни
*/
Monday,/* понедельник */
Tuesday,/* вторник */
Wednesday, /* среда */
Thursday,/* четверг */
.Friday )/* пятница */
/* описание перечисления в языке
С */
enumeweekdaysewToday;
/* описание перечисления в C++ */
eweekdaysewToday;
/* задание цикла for без использования
перечисления */
for(i=0;i<= 4; i++)
.
.
.
/* и с использованием перечисления */
for(ewToday = Monday; ewToday <= Friday; ewToday++)
Компилятор не видит разницы между типами данных intи enum. Поэтому переменным типа перечисления в программе могут присваиваться целочисленные значения. Но в языке C++, если такое присваивание не сопровождается явным приведением типа, компилятор выдаст предупреждение:
/* допустимо в С, но не в C++ */ ewtodау = 1;
/* устранение ошибки в C++ */ ewToday = (eweekdays) 1;
Перечисления часто используются в тех случаях, когда данные можно представить в виде нумерованного списка, например содержащего названия месяцев года или дней недели. В следующем примере создается перечисление emonths, включающее названия месяцев.
/*
* enum. с
* Эта программа на языке С демонстрирует работу с перечислением.
*/
#include <stdio.h>
enum emonths {
January = 1 ,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December
} months;
int main(void)
{
int ipresent_month;
int idiff;
{
printf("Введите номер текущего месяца (от1 до 12): ") ; scanf("%d",&ipresent_month);
months = December;
idiff = (int)months - ipresent_month;
printf("\nfloконца года осталось %dмесяца(ев)An", idiff);
return (0);
}
В данной программе перечисление в действительности представляет собой ряд целых чисел от 1 до 12. Например, значение переменной months, после того как ей была присвоена константа December, стало равным 12. Поскольку названию каждого месяца соответствует определенное числовое значение, то элементы перечисления могут участвовать в арифметических операциях.
В результате выполнения программы на экран будет выведена примерно следующая информация:
Введите номер текущего месяца (от
1 до 12): 4
До конца года осталось 8 месяца(ев).