Сегодня мы рассмотрим три совершенно невзаимосвязанные темы: структуры, перечисления и объединения. Так как они не слишком сложные, мы рассмотрим их все вместе.
Структуры (struct)
Использование структур обусловлено необходимостью объединить несколько переменных.
Структуры - это, обычно, группа переменных описывающих какую-нибудь сущность.
Например, в игре танк можно описать группой переменных: количество снарядов, количество горючего и т.д. У нас в примере танк будет обладать следующими параметрами: координаты и количество топлива (от нуля до двадцати).
Зачем нам использовать какие-то структуры спросите Вы? Ведь переменные и так прекрасно хранятся.
Причин две: переменные в структурах хранятся в одном месте и можно создавать несколько структурных переменных и у всех у них будут одинаковые характеристики.
Объявления у структуры нет. Её нужно сразу определять. Тело структуры должно находиться до начала main.
PHP код:
struct tank
{
int x, y;
int fuel;
};
Здесь мы видим структуру с именем tank. Перед именем стоит ключевое слово struct (от structure - структура). В теле структуры находятся объявления переменных отвечающих за координаты и кол-во топлива. Обратите внимание, что после закрывающей фигурной скобки стоит точка с запятой. Не забывайте о ней в своих структурах.
На самом деле мы только что создали новый тип данных. Такие типы данных - определяемые программистом, называются пользовательскими. К пользовательским типам данных, помимо структур, относятся и классы, но об этом в следующий раз.
Когда мы определяем структуру, под неё не выделяется память. Структура - это как шаблон. Теперь на основе структуры tank можно создать много структурных переменных. Все эти переменные будут иметь тип tank, и каждая структурная переменная будет иметь доступ к своим переменным x, y, fuel.
Сейчас нам нужно определить переменную типа tank. Или даже две:
PHP код:
tank t34 = {0,0,20}; // x, y, fuel
tank pz4 = {8,7,20};
Обе переменные мы сразу проинициализировали (хотя этого можно было и не делать). Для этого использовали список значений в фигурных скобках. Значения в скобках присваиваются переменным внутри структуры в том порядке, в котором они были объявлены при определении структуры: x, y, fuel.
Теперь у нас есть две переменные t34, pz4 типа tank. У каждой есть поля x, y, fuel. Подчёркиваю красным: каждая структурная переменная обладает своим набором переменных, которые были объявлены в структуре.
Чтобы получить доступ к полю, нужно воспользоваться операцией доступа (операцией точки). Например:
PHP код:
pz4.fuel = 100;
cout << pz4.fuel << "\n";
Сначала мы указываем имя структурной переменной, затем ставим точку и в конце указываем имя поля структуры. Здесь мы присвоили значение полю fuel структурной переменной pz4, а затем вывели значение поля fuel на экран.
Напоследок, вот как получить доступ ко всем полям двух структурных переменных:
PHP код:
t34.x;
t34.y;
t34.fuel;
pz4.x;
pz4.y;
pz4.fuel;
Структурные переменные в памяти компьютера
А вот как структурные переменные располагаются в памяти компьютера (адреса в шестнадцатиричном формате):
Каждая переменная типа int занимает в памяти четыре байта. Вся структурная переменная структуры tank займёт в памяти 12 байт - три переменные типа int.
Адрес переменной - младший байт этой переменной. Например, адрес переменной t34.fuel - 0012FEA2.
Кроме того стоит заметить, что адрес самой стуктурной переменной t34 совпадает с адресом первого поля x. Т.е. адрес t34.x равен 0012FE94, адрес t34 равен 0012FE94.
Также обратите внимание, что все поля t34 расположены друг за другом. Заканчивается переменная x (адрес 0012FE97) и следующий байт уже принадлежит y (0012FE98), заканчивается y (адрес 0012FEA1) и сразу же начинается fuel (адрес 0012FEA2). Запомните, поля структурной переменной хранятся в памяти друг за другом, между ними нет никаких других данных.
И ещё одно, две структурные переменные t34 и pz4 необязательно расположены в памяти рядом. В данном примере между ними больше 140-а байтов. Кроме того, переменная pz4 расположена в памяти "раньше" - адрес этой переменной меньше адреса t34.
Хорошенько разберитесь со всем этим. Когда дойдём до рассмотрения указателей, вы поймёте, зачем необходимо понимание того, как устроена память.
Перечисления (enum)
Перечисления - это ещё один пользовательский тип данных. Перечисления используют для описания какого-то небольшого множества значений.
С помощью перечислений можно задать дни недели, месяцы, ну или что-нибудь подобное. В качестве примера рассмотрим стороны света. Стороны света на экране расположены так: вверху - север (north), справа - восток (east), слева - запад (west) и внизу - юг (south).
PHP код:
enum cardinal_dirs { north, west, east, south };
Здесь мы определили перечисление cardinal_dirs. В начале строки стоит ключевое слово enum (enumeration - перечисление). Затем указывается имя перечисления, после которого, через пробел, в фигурных скобках задаются значения, которые смогут принимать переменные типа cardinal_dirs. После фигурных скобок ставится точка с запятой.
После того, как определено перечисление, можно создавать переменные нового типа:
PHP код:
cardinal_dirs ch = north;
Обратите внимание, что переменные типа cardinal_dirs могут принимать только четыре значения: north, south, east, west. Кроме того, элементы в перечислении нумеруются от нуля. Т.е. north = 0, east = 1, south = 2, west = 3. Вместо перечисления мы могли бы создать четыре константы:
PHP код:
const int north = 0;
const int east = 1;
const int south = 2;
const int west = 3;
Результат одинаковый.
Как видите использовать перечисления довольно легко. Но стороны света у нас закодированы клавишами стрелочек, поэтому нам нужно инициализировать элементы перечисления числами. Как я уже писал, отсчёт ведётся с нуля. Но, к счастью, это можно изменить. Если мы переопределим какой-либо элемент значением 75, то следующий, получит значение 76. Нам нужно переопределить все четыре значения.
PHP код:
enum cardinal_dirs { north = 72, west = 75, east = 77, south = 80 };
Про структуры и перечисления всё. Есть ещё некоторые важные моменты по структурам, но нам они пока не нужны.
Ещё раз повторюсь, точно такого же результата можно было бы добиться с помощью четырёх констант.
Объединения (union)
Объединения используются когда необходимо получить доступ к одним и тем же данным разными способами.
Допустим мы хотим вывести все поля структуры tank. Пока что у нас есть только один способ сделать это:
PHP код:
cout << t34.x << "\n";
cout << t34.y << "\n";
cout << t34.fuel << "\n";
Гораздо удобнее это было бы сделать с помощью цикла. Но тогда нам пришлось бы использовать массив, а определение структуры выглядело бы вот так:
PHP код:
struct tank
{
int t[3];
};
Теперь вывести структурную переменную на экран проще:
PHP код:
for (int i; i < 3; i++)
{
cout << t34.t[i] << "\n";
}
Но при использовании массивов переменная потеряла в гибкости, и код стал не таким понятным:
PHP код:
t34.x = 3; // До..
t34.y = 4;
t34.t[0] = 3; // ... и после.
t34.t[1] = 4;
Можно объединить эти два способа с помощью объединения. :)
При этом структура tank будет выглядеть вот так:
PHP код:
union tank
{
struct
{
int x,y;
int fuel;
};
int t[3];
};
Во-первых мы заменили struct на union (union - объединение). Внутри объединения расположена безымянная структура в которой определены все те переменные, которые были в структуре tank.
После безымянной структуры определён массив целых чисел из трёх элементов.
Теперь создадим переменную и обратимся к её полям:
PHP код:
tank t34;
t34.x = 5;
t34.y = 1;
t34.fuel = 20;
cout << t34.t[0] << "\n";
cout << t34.t[1] << "\n";
cout << t34.t[2] << "\n";
На экран будет выведено:
Дело в том, что x и t[0] - разные имена одного и того же участка памяти, также как и: y и t[1], fuel и t[2].
Вот как переменная типа tank расположено в памяти:
union tank имеет тот же адрес, что и x и t[0], просто у нас картинки рисует чрезвычайно толковый парень, поэтому получается так.
На картинке перед каждым адресом стоит: 0x (ноль икс). Это обозначение используется с шестнадцатиричными числами, т.е. если перед числом вы увидите 0x - то оно представлено в шестнадцатиричном формате.
А что будет если не определять безымянную структуру, а переменные располагать следующим образом:
PHP код:
union tank
{
int x;
int y;
int fuel;
int t[3];
};
Ничего хорошего из этого не выйдет. Здесь x, y, fuel и t[0] - имена одного участка памяти.
В будущем мы будем использовать объединения следующим образом:
PHP код:
struct tank
{
union
{
struct
{
int x,y;
int fuel;
};
int t[3];
};
};
Но вместо слово struct в первой строке будет стоять class. Сейчас вот этот вариант (struct tank) и предыдущий (union tank) работают одинаково.
Применение объединений очень сильно ограничено. Мы их рассмотрели, так как они особенно часто встречаются в математических библиотеках.