Тема 12. СТРУКТУРЫ
Зачем нужны структуры?
Одна из задач любого языка программирования заключается в предоставлении программисту инструментов, позволяющих представлять данные в удобной для работы форме. Структуры являются одним из таких инструментов, который хорошо подходит в ряде случаев, когда сложно обойтись только переменными и массивами. Некоторые языки программирования, например, Паскаль, применительно к структурам используют термин запись (record), тем не менее, принципы работы с записями и структурами одинаковы.
Предположим, что перед нами стоит задача реализовать базу данных, предназначенную для работы с информацией о сотрудниках некоторой фирмы. С каждым сотрудником связан набор разнотипных данных, таких как имя (name), возраст (age), зарплата (salary) и др. Чтобы решить эту задачу, можно описать несколько массивов, каждый из которых будет отвечать за свою характеристику сотрудника:
char name[][20];
float salary[];
int age[];
Однако такой способ совсем неудобен при большом количестве характеристик товара и, следовательно, большом количестве массивов. Представьте себе, как будет выглядеть сортировка десяти массивов, если понадобится упорядочить сотрудников по возрасту; очевидно, что сортировать массивы придется синхронно, чтобы элементы с одинаковыми индексами относились к одному человеку.
Более естественным способом организации данных является представление всех сотрудников в виде массива записей, каждая из которых содержит всю необходимую информацию о сотруднике. Другими словами, вместо трех одномерных массивов следует воспользоваться одним массивом, где каждый из элементов является записью с тремя полями. Структура как раз и представляет собой запись, состоящую из разнотипных данных, относящихся к одному объекту.
Создание структуры
Рассмотрим, как описать структуру для представления сотрудника:
struct Worker {
stirng name;
float salary;
int age;
};
В данном фрагменте кода struct – это ключевое слово, информирующее о начале описания структуры. Далее указывается имя структуры Worker, а затем в фигурных скобках перечисляются переменные, входящие в структуру, и указывается их тип; применительно к структурам такие переменные называются полями. Описав структуру Worker, мы фактически определили новый составной тип данных, называемый Worker. После его определения можно описать переменную этого типа:
Worker director;
При описании также можно задать значения полей структуры, перечислив эти значения в том порядке, который указан при описании структуры:
Worker director = {"Андрей", 34, 1000.6};
Заметим, что при описании структуры Worker память под нее не выделяется; это происходит только при описании переменной director типа Worker, которая занимает столько места, сколько ее поля все вместе. Проверим это с помощью оператора sizeof:
cout << "Тип\tРазмер\n");
cout << "----------------------\n";
cout << 20*sizeof(char) << endl;
cout << sizeof(int) << endl;
cout << sizeof(float) << endl;
cout << "----------------------\n");
cout << sizeof(Worker) << endl;
Строка длины 20 занимает 20 байт; целое и вещественное числа занимают по 4 байта; итого получается 28 байт:
Обратите внимание на то, что во избежание путаницы имена структур следует начинать с заглавной буквы, а имена переменных – со строчной. Такое соглашение позволяет к тому же объявлять переменные и структуры с «почти» одинаковыми именами, поскольку язык Си различает регистр букв:
Worker worker;
Обращение к полям структуры и оператор «точка»
Обратиться к значению, которое хранит переменная, можно, просто написав ее имя. Структура является не просто переменной, а в некотором смысле составной переменной, поэтому для обращения к данным, которые хранит структура необходимо указать ее имя и имя поля. Для этого существует специальный оператор «точка», который разделяет имена структуры и ее полей. Рассмотрим пример использования этого оператора для того, чтобы присвоить значения элементам структуры:
Worker manager;
manager.name = "Сергей";
manager.salary = 1000.55;
manager.age = 33;
Обратите внимание на то, что поля manager.salary и manager.age являются обычными переменными, и работать с ними нужно, как с переменными. Поле manager.name является строкой.
Ввод и вывод содержимого структуры
Вывод значений полей структуры осуществляется аналогично переменным с учетом их типа:
printf("Имя: %s\n", manager.name);
printf("Возраст: %d\n", manager.age);
printf("Зарплата: %g\n", manager.salary);
При вводе необходимо ставить амперсанд:
scanf("%d", & manager.age);
Оператор точка имеет более высокий приоритет, поэтому ставить скобки, т.е. писать &(manager.age) не нужно. К строке manager.name применимы функции gets() и puts():
gets(manager.name);
puts(manager.name);
Можно также обратиться к отдельным буквам этой строки:
printf("Имя начинается с буквы %c\n",
manager.name[0]);
Вложенные структуры
Пусть для каждого сотрудника требуется хранить его адрес, который состоит из нескольких полей: улица (street), номер дома (house) и номер квартиры (flat). В таком случае адрес разумно также представить в виде структуры
struct Address {
char street[50];
int house;
int flat;
};
и одним из полей структуры Worker сделать структуру Address
struct Worker {
char name[20];
int age;
float salary;
Address address;
};
Обращение к полям вложенной структуры Address происходит через поля структуры Worker; например, задать адрес сотрудника и вывести его на экран можно так:
strcpy(manager.address.street, "Ленина");
manager.address.house = 10;
manager.address.flat = 7;
printf("Улица: %s\n", manager.address.street);
printf("Дом: %d\n", manager.addrress.house);
printf("Квартира: %d\n",manager.address.flat);
Массив структур
Как мы отмечали в самом начале, важной причиной введения структур является желание работать со списком записей. Такой список представляется в виде массива структур. Аналогично обычному массиву, где элементами являются числа или символы, в массиве структур элементами являются структуры. Рассмотрим, как объявляется массив структур, в котором будет храниться информация о сотрудниках.
#define SIZE 5
Worker workers[SIZE];
Значения полям элементов массива структур можно задать при его объявлении, перечислив поля в том порядке, который указан при объявлении структуры:
Worker workers[SIZE] = {
{"Андрей", 30, 5000},
{"Светлана", 51, 2000},
{"Дмитрий", 45, 3000},
{"Анна", 28, 4000},
{"Василий", 37, 1000}
};
а можно и после объявления:
strcpy(workers[0].name, "Игорь");
workers[0].age = 53;
workers[0].salary = 2500;
strcpy(workers[1].name, "Демьян");
workers[1].age = 57;
workers[1].salary = 1500;
Причем грамотная группировка элементов выглядит именно так, как показано выше, а не так:
//Неправильная группировка!
strcpy(workers[0].name, "Игорь");
strcpy(workers[1].name, "Демьян");
workers[0].age = 53;
workers[1].age = 57;
workers[0].salary = 2500;
workers[1].salary = 1500;
поскольку структура предполагает группировку разнотипных данных, относящихся к одному объекту, а не группировку данных согласно их типу.