Skip to main content
0 votes

Доброго времени суток! Надеюсь, что предыдущая неделя прошла весьма успешно, и вы готовы к продолжению изучения основ программирования на С++. Мы уже успели изучить помимо всего прочего все простые типы данных, и сегодня перейдем к более сложным вещам, в частности, к структурам.
Переменные идеальны когда надо хранить одно значение определенного типа, массивы хороши когда однотипных значений много, а что делать если необходимо как-то связывать между собой разнотипные данные? Тут-то нам структуры и пригодятся. Можно сказать, что структура – объединение простых переменных.
Как и любую переменную, структуру сначала необходимо объявить (определить). Описание структуры начинается с ключевого слова struct после которого указывается имя структуры, и перечисления всех переменных, принадлежащих структуре. Место описания структуры – перед функцией main. Для примера создадим структуру под названием «клиент». У нашего клиента будет возраст, имя, номер телефона и состояние счета.

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. struct client
  8. {
  9. string name;
  10. int age;
  11. int phoneNumber;
  12. float cash;
  13. };
  14.  
  15. int _tmain(int argc, _TCHAR* argv[])
  16. {
  17. return 0;
  18. }

Описание структуры не может содержать ничего кроме описания переменных, входящих в эту структуру. То есть мы описываем лишь ее «состав». В дальнейшем мы каждую переменную, входящую в состав структуры, будем именовать полем структуры. Таким образом, структура client содержит поля name, age, phoneNumber и cash.
Мы описали структуру, но сразу с ней работать нельзя, потому что компилятор еще не выделил память для нашего клиента. Так выделим же для нее память, сделаем это точно так же как и для обычной переменной:

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. struct client
  8. {
  9. string name;
  10. int age;
  11. int phoneNumber;
  12. float cash;
  13. };
  14.  
  15. int _tmain(int argc, _TCHAR* argv[])
  16. {
  17. client cli;
  18. return 0;
  19. }

Почему мы так сделали: когда мы создаем, например, целочисленную переменную, мы указываем тип переменной и ее имя. С именем вопросов возникнуть не должно, поэтому сразу к типу. Каждый тип данных для своего хранения требует определенных объемов памяти, и поэтому когда мы пишем, что хотим переменную целочисленную, компилятор выделяет нам один объем памяти, а если мы хотим вещественную (дробную) переменную, то компилятор нам выделяет больше памяти для хранения еще и дробной части. Тут ситуация аналогичная: при описании структуры мы указали все поля и их типы, следовательно, компилятору остается выделить под структуру памяти столько, сколько занимает каждое ее поле. Именно поэтому часть программы, написанная выше, является полностью легальной.
Тут разобрались, хорошо. Как работать с полями структуры. Поле структуры – простая переменная, следовательно, принцип работы с ними абсолютно идентичен. Только необходимо получить доступ к каждому полю структуры. Делается это через операцию точки. Давайте проинициализируем все поля переменной cli.

  1. struct client
  2. {
  3. string name;
  4. int age;
  5. int phoneNumber;
  6. float cash;
  7. };
  8.  
  9. int _tmain(int argc, _TCHAR* argv[])
  10. {
  11. client cli;
  12. cli.name = "Ivan";
  13. cli.age = 28;
  14. cli.phoneNumber = 123456;
  15. cli.cash = 32000;
  16. return 0;
  17. }

Можно инициализировать каждое поле переменной отдельным оператором, а можно сделать это сразу «скопом». Кстати, очень ярко выражается удобство использования специализированных IDE (integrated development environment – интегрированная среда разработки) по сравнению, скажем, с обычным блокнотом. А это лишь малая часть возможностей, ну да не будем отвлекаться.

help.png

  1. #include "stdafx.h"
  2. #include <iostream>
  3. #include <string>
  4.  
  5. using namespace std;
  6.  
  7. struct client
  8. {
  9. string name;
  10. int age;
  11. int phoneNumber;
  12. float cash;
  13. };
  14.  
  15. int _tmain(int argc, _TCHAR* argv[])
  16. {
  17. client cli;
  18. cli.name = "Ivan";
  19. cli.age = 28;
  20. cli.phoneNumber = 123456;
  21. cli.cash = 32000;
  22. client cli2 = { "Petr", 35, 654321, 45500 };
  23. return 0;
  24. }

Переменная cli2 инициализирована своими значениями, но результат инициализации аналогичен первому рассмотренному.
Раз можно работать со структурами как с обычными переменными, то, наверное, можно их присваивать друг другу? Можно.

  1. struct client
  2. {
  3. string name;
  4. int age;
  5. int phoneNumber;
  6. float cash;
  7. };
  8.  
  9. int _tmain(int argc, _TCHAR* argv[])
  10. {
  11. client cli;
  12. client cli2 = { "Petr", 35, 654321, 45500 };
  13. cli = cli2;
  14. cout << cli.name << " " << cli.age << " " << cli.phoneNumber << " " << cli.cash << endl;
  15. getchar();
  16. return 0;
  17. }

Как мы видим, переменная cli2 инициализирована, а переменная cli – нет. Переменной cli присваивается переменная cli2, в результате чего все поля cli принимают значения соответствующих полей cli2. К сожалению, для вывода значений приходится явно указывать поля, которые необходимо вывести, и если необходимо вывести все поля, то придется их указать. Результат работы программы приведен ниже.

struct out.PNG

Так же как и ветвления с циклами, структуры можно вкладывать друг в друга. Давайте изменим нашу структуру таким образом, чтобы она не только показывала состояние счета клиента, но и валюту.

  1. struct money
  2. {
  3. float cash;
  4. string currency;
  5. };
  6.  
  7. struct client
  8. {
  9. string name;
  10. int age;
  11. int phoneNumber;
  12. money cash;
  13. };
  14.  
  15. int _tmain(int argc, _TCHAR* argv[])
  16. {
  17. client cli;
  18. cli.name = "Ivan";
  19. cli.age = 28;
  20. cli.phoneNumber = 123456;
  21. cli.cash.cash = 32000;
  22. cli.cash.currency = "rubles";
  23. client cli2 = {"Petr", 35, 654321, {12000, "eu"}};
  24. cout << cli.name << " " << cli.age << " " << cli.phoneNumber << " " << cli.cash.cash << " " << cli.cash.currency << endl;
  25. cout << cli2.name << " " << cli.age << " " << cli.phoneNumber << " " << cli.cash.cash << "" << cli.cash.currency << endl;
  26. getchar();
  27. return 0;
  28. }

Как мы видим, присутствует структура в структуре. Доступ к полям вложенной структуры осуществляется так же как и к полям внешней структуры. Инициализация проходит аналогичным образом (client cli2 = {"Petr", 35, 654321, {12000, "eu"}};). Структуры можно вкладывать друг в друга практически до бесконечности, но не дай бог вам этим заняться.
Также мы видим небольшое новшество: cli.cash.cash. Почему cash встречается 2 раза, нельзя ведь объявлять 2 переменные с одинаковым именем. Вообще-то можно, но они должны быть в разных «блоках» программы. То есть мы объявили cash в клиенте и другой cash в money. И тут следовало бы сказать о таком понятии как область видимости. Переменная может быть локальной или глобальной. Глобальная переменная видна в каждой точке программы. Локальная – только в рамках данного блока. Что значит «переменная видна»? Значит, что мы можем к ней обращаться и выполнять различные операции. О глобальных и локальных переменных мы пока что только упомянули, рассмотрим подробно их в одном из наших следующих уроков. Сейчас нам надо знать, что cash в client виден только для клиента, а cash в money виден только в money.
Что еще можно сказать о структурах. Раз структура всего лишь объединение простых переменных, то можно ли создать массив структур? Можно. Без лишних слов сразу к коду. Дабы не усложнять задачу, возьмем структуру money, с которой мы уже работали, создадим массив из трех структур, проинициализируем его и выведем.

  1. struct money
  2. {
  3. float cash;
  4. string currency;
  5. };
  6.  
  7. int _tmain(int argc, _TCHAR* argv[])
  8. {
  9. money str[3];
  10. for (int i = 0; i <= 2; i++)
  11. {
  12. str[i].cash = 1000 * i;
  13. str[i].currency = "rub";
  14. cout << str[i].cash << " " << str[i].currency << endl;
  15. }
  16. getchar();
  17. return 0;
  18. }

В случае массива запись типа str[i] = {1000 * i, “rub”}; уже не работает, приходится все поля инициализировать по-отдельности. Также представлена в чем-то новая для нас запись цикла. До этого дня вы бы, вероятно, написали for (int i = 0; i < 3; i++). Так в чем же разница? Ни в чем, эти записи абсолютно идентичны по своей сути. Если мы используем знак строгого равенства, условие будет работать при числах строго меньших заданного значения, а при нестрогом равенстве цикл отработает на 1 раз больше (при значении параметра равном заданному значению). Честно говоря, никогда не натыкался на рекомендации какая запись лучше и когда какую использовать, так что это идет от программиста, просто будьте внимательны, чтобы случайно не записать на 1 элемент массива больше, чем нужно.
Так или иначе, результат работы программы корректен и ожидаем и представлен на скриншоте ниже.

mass of struct.PNG

В целом работа с массивом структур идентична работе с массивами, как вы, наверное, уже догадались.
На сегодня, пожалуй, я рассказал достаточно, надеюсь, что все было предельно понятно, если же нет, жду вопросов в комментариях. До следующего занятия есть еще одна неделя, и по традиции будет выдано домашнее задание. Необходимо создать массив структур «Команда», содержащую поля: «Название», «Количество игр», «Очки», «Забитые мячи», «Пропущенные мячи», «Квалифицирована?» Заполнить данными, вывести на экран. Надеюсь, что при выполнении задания не возникнет проблем. Удачи и до встречи на следующем уроке.