Урок №61. Структуры

  Юрий  | 

  Обновл. 21 Апр 2019  | 

 50888

 ǀ   27 

В программировании есть много случаев, когда может понадобиться больше одной переменной для представления определённого объекта.

Зачем нужны структуры?

Например, для представления самого себя, вы, скорее всего, захотите указать своё имя, день рождения, рост, вес или любую другую информацию:

Но теперь у вас есть 6 отдельных независимых переменных. Если вы захотите передать информацию о себе в функцию, то вам придётся передавать каждую переменную по отдельности. Кроме того, если вы захотите хранить информацию о ком-то ещё, то вам придётся дополнительно объявить ещё 6 переменных на каждого человека! Невооружённым глазом видно, что такая реализация не очень эффективна.

К счастью, C++ позволяет программистам создавать свои собственные пользовательские типы данных — типы, которые группируют несколько отдельных переменных вместе. Одним из простейших пользовательских типов данных является структура. Структура позволяет сгруппировать переменные разных типов в единое целое.

Объявление и определение структур


Поскольку структуры определяются программистом, то вначале мы должны сообщить компилятору, как она вообще будет выглядеть. Для этого используется ключевое слово struct:

Мы определили структуру с именем Employee. Она содержит 3 переменные: id типа short, age типа int и salary типа double. Эти переменные, которые являются частью структуры, называются членами структуры (или ещё «полями структуры»). Employee — это простое объявление структуры. Хоть мы и указали компилятору, что она имеет переменные-члены, память под неё сейчас не выделяется. Имена структур принято писать с заглавной буквы, чтобы отличать их от имён переменных.

Предупреждение: Одной из самых простых ошибок в C++ является забыть точку с запятой в конце объявления структуры. Это приведёт к ошибке компиляции в следующей строке кода. Современные компиляторы, такие как Visual Studio 2010 и выше, укажут вам, что вы забыли точку с запятой в конце, но более старые компиляторы могут этого и не сделать, из-за чего такую ошибку будет трудно найти. О том, как установить Visual Studio или какую выбрать IDE мы уже говорили в уроке №4.

Чтобы использовать структуру Employee, нам нужно просто объявить переменную типа Employee:

Здесь мы определили переменную типа Employee с именем john. Как и в случае с обычными переменными, определение переменной структуры приведёт к выделению для неё памяти.

Объявить можно и несколько переменных одной структуры:

Доступ к членам структур

Когда мы объявляем переменную структуры, например, Employee john, то john ссылается на всю структуру. Для того, чтобы получить доступ к отдельным её членам, используется оператор выбора члена (.). Например, в примере ниже мы используем оператор выбора членов для инициализации каждого члена структуры:

Как и в случае с обычными переменными, переменные-члены структуры не инициализируются автоматически и обычно содержат мусор. Инициализировать их нужно вручную.

В примере выше легко определить, какая переменная относится к структуре John, а какая к структуре James. Это обеспечивает гораздо более высокий уровень организации, чем в случае с обычными отдельными переменными.

Переменные-члены структуры работают так же, как и простые переменные, поэтому с ними можно выполнять обычные арифметические операции и операции сравнения:

Инициализация структур


Инициализация структур путём присваивания значений каждому члену по порядку — занятие довольно громоздкое (особенно, если этих членов много), поэтому в C++ есть более быстрый способ инициализации структур с помощью списка инициализаторов. Он позволяет инициализировать некоторые или все члены структуры во время объявления переменной типа struct:

В C++11 также можно использовать инициализацию uniform:

Если в списке инициализаторов не будет одного или нескольких элементов, то им присвоятся значения по умолчанию (обычно 0). В примере выше, члену james.salary присваивается значение по умолчанию 0.0, так как мы сами не предоставили никакое значение во время инициализации.

C++11/14: Инициализация нестатических членов структур

В C++11 добавили возможность присваивать нестатическим (обычным) членам структуры значения по умолчанию. Например:

К сожалению, в C++11 синтаксис инициализации нестатических членов структуры несовместим с синтаксисом списка инициализаторов или инициализацией uniform. В C++11 следующая программа не скомпилируется:

Следовательно, вам нужно будет решить, хотите ли вы использовать инициализацию нестатических полей или инициализацию uniform. Инициализация uniform более гибкая, поэтому мы рекомендуем использовать именно её.

Однако в C++14 это ограничение было снято, и оба варианта можно использовать. Мы ещё поговорим детальнее о статических членах структуры в соответствующем уроке.

Присваивание значений членам структур


До C++11, если бы мы захотели присвоить значения членам структуры, то нам бы пришлось это делать вручную каждому члену отдельно:

Это боль, особенно когда членов в структуре много. В C++11 вы можете присваивать значения членам структур, используя список инициализаторов:

Структуры и функции

Большим преимуществом использования структур, нежели отдельных переменных, является возможность передать всю структуру в функцию, которая должна работать с её членами:

В примере выше мы передали структуру Employee в функцию printInformation(). Это позволило нам не передавать каждую переменную по отдельности. Более того, если мы когда-либо захотим добавить новых членов в структуру Employee, нам не придётся изменять объявление или вызов функции!

Результат выполнения программы выше:

ID: 21
Age: 27
Salary: 28.45

ID: 22
Age: 29
Salary: 19.29

Функция также может возвращать структуру (это один из тех немногих случаев, когда функция может возвращать несколько переменных). Например:

Результат выполнения программы выше:

The point is zero

Вложенные структуры


Одни структуры могут содержать другие структуры. Например:

В этом случае, если бы мы хотели узнать, какая зарплата в CEO (исполнительного директора), то нам бы пришлось использовать оператор выбора членов дважды:

Сначала мы выбираем поле CEO из структуры myCompany, а затем поле salary из структуры Employee.

Вы можете использовать вложенные списки инициализаторов c вложенными структурами:

Размер структур

Как правило, размер структуры — это сумма размеров всех её членов, но не всегда!

Например, рассмотрим структуру Employee. На большинстве платформ тип short занимает 2 байта, тип int — 4 байта, а тип double — 8 байт. Следовательно, ожидается, что Employee будет занимать 2 + 4 + 8 = 14 байт. Чтобы узнать точный размер Employee, мы можем воспользоваться оператором sizeof:

Результат выполнения программы выше (на компьютере автора):

The size of Employee is 16

Оказывается, мы можем сказать только, что размер структуры будет, по крайней мере, не меньше суммы размеров всех её членов. Но он может быть и больше! По соображениям производительности компилятор иногда может добавлять «пробелы/промежутки» в структуры.

В структуре Employee компилятор неявно добавил 2 байта после члена id, увеличивая размер структуры к 16 байтам вместо 14. Причина, по которой это происходит, выходит за рамки этого туториала, но если вы хотите знать больше, то можете прочитать о выравнивании данных в Википедии.

Доступ к структурам из нескольких файлов

Поскольку объявление структуры не провоцирует выделение памяти, то использовать предварительное объявление для неё вы не сможете. Но есть обходной путь: если вы хотите использовать объявление структуры в нескольких файлах (чтобы иметь возможность создавать переменные этой структуры в нескольких файлах), поместите объявление структуры в заголовочный файл и #include этот файл везде, где необходимо использовать структуру.

Переменные типа struct подчиняются тем же правилам, что и обычные переменные. Следовательно, если вы хотите сделать переменную структуры доступной в нескольких файлах, то вы можете использовать ключевое слово extern.

Заключение

Структуры очень важны в C++, поскольку их понимание — это первый крупный шаг к объектно-ориентированному программированию! Позже, в этих уроках, вы узнаете о другом пользовательском типе данных — классе, который является продолжением темы структур.

Тест

Задание №1

У вас есть веб-сайт и вы хотите отслеживать, сколько денег вы зарабатываете в день от размещённой на нём рекламы. Объявите структуру Advertising, которая будет отслеживать:

   сколько объявлений вы показали посетителям (1);

   сколько процентов посетителей нажали на объявления (2);

   сколько вы заработали в среднем за каждое нажатие на объявления (3).

Значения этих трёх полей должен вводить пользователь. Передайте структуру Advertising в функцию, которая выведет каждое из этих значений, а затем подсчитает, сколько всего денег вы заработали за день (перемножьте все 3 поля).

Ответ №1

Задание №2

Создайте структуру для хранения дробных чисел. Структура должна иметь 2 члена: целочисленный числитель и целочисленный знаменатель. Объявите две дробные переменные и получите их значения от пользователя. Напишите функцию multiply() (параметрами которой будут эти две переменные), которая будет перемножать эти числа и выводить результат в виде десятичного числа.

Ответ №2

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (165 оценок, среднее: 4,91 из 5)
Загрузка...

Комментариев: 27

  1. Аватар Игорь:

    задача №2

  2. Аватар Игорь:

  3. Аватар Ruslan:

    Немного не подумал, что вы хотели во втором задании просто перемножить два числа, и сделал так:

  4. Аватар Алексей:

    Вот честно, не понял я второго задания.
    Тоже ничего сложного по структуре, но написано одно — делать другое.

  5. Аватар Алексей:

    Сильный был "завтык" с объявлением структуры в функцию.

    Сделал муторно, потом подправил, но думаю это все равно сложно написано. Задание не сложное, все те же переменные и функции, добавление данных, но вот…

  6. Аватар Алексей:

    Структуры сильно напоминают массивы.
    Тут конечно же предоставляются имена для каждой, но все же.

  7. Аватар Екатерина:

    Добрый день. Нравятся ваши уроки. Не думаете выпустить печатный вариант?) Легко даётся материал по вашим урокам..и было бы удобно иметь подобного рода настольную книгу)

    1. Юрий Юрий:

      Привет, чуть позднее будет .pdf версия всех уроков по С++.

  8. Аватар Константин:

    Юра, прочитай внимательно:
    C++ 11/14: Не статическая инициализация членов
    Начиная с C++ 11, появляется возможность задавать не статистическим (обычным) членам структуры значения по умолчанию:
    — если это опечатка — исправляй, если — нет — давай урок по статистике.

    1. Юрий Юрий:

      Спасибо, исправил.

      В ближайшие месяцы пересмотрю полностью все уроки.

  9. Аватар somebox:

    Только посмотрев решение, понял, что нужно указать не дробные, а "дробные" переменные. А то не мог понять, причем тут double. Из-за этого не решил второе задание. Мне кажется, чуть внятнее нужно давать условия задачи.

  10. Аватар Дмитрий:

    Решение на первый тест:

  11. Аватар Алексей:

    Ответ к заданию номер 2:

  12. Аватар Максим:

  13. Аватар Эдуард:

    Такой код уместен ко второму заданию? ( Да, знаю что используются "магические числа", а чем их можно заменить, если не использовать цикл?

  14. Аватар Torgu:

    Здравствуйте, увидев снятие ограничения на инициализацию структур в C++14, я решил узнать, используется ли у меня C++14. В интернете везде один и тот же способ — написать программу:

    Если программа вывела 199711, то С++98/03, если 201103 — С++11, а если 201300 или 201402, то C++14. У меня вывело 199711, то есть даже C++11 нет. Но тем не менее работает уникальная инициализация, свойственная для C++11. В чем проблема? Как точно определить версию C++? Использую Microsoft Visual Studio Community 2017, все по гайду

  15. Аватар master114:

    Мне кажется, что в первом задании Значению количества просмотров лучше не int задавать, а что-нибудь побольше (я long присвоил).
    Ведь обычно количество просмотров рекламы больше, чем 127.
    Иначе будет переполнение и результат мусорный???

    1. Юрий Юрий:

      Переменная int, скорее всего, на вашем компьютере занимает 4 байта (у меня 4). Диапазон значений для 4 байтов — от -2 147 483 648 до 2 147 483 647 — всего должно хватать.

  16. Аватар Серж:

    Пример программы к заданию номер 2 (проверил ,работает)

    1. Юрий Юрий:

      Как вариант да, может быть.

  17. Аватар Максим:

    Действительно. Спасибо за Вашу работу. Мои варианты решения.
    Первая:

    Вторая:

    И для себя лично хотелось бы спросить/попросить примеры реальных рабочих задач, какие действительно нужно уметь решать на момент собеседования и на начальных этапах работы.

    1. Юрий Юрий:

      У вас как в первом, так и во втором задании используется возврат сразу нескольких значений (через оператор return). Это неверно, оператор return должен возвращать одно значение.

      Насчет задачек — поищите в Интернете или в Ютубе. Предоставить вам реальные задачки, которые используются на собеседовании я не могу, так как сам на собеседования по С++ не ходил.

      1. Аватар Максим:

        А почему такое ограничение на return? Код ведь рабочий и значения передаются как положено.

        1. Юрий Юрий:

          Например:

          Выведется только одно последнее значение, которое вы введете, так как return может возвращать только одно значение. Когда вам нужно будет использовать возврат значений из функции, как в примере выше, то что тогда будете делать? Есть другие способы возврата сразу нескольких значений из функции, не нарушая при этом правил (смотрите урок 98).

  18. Аватар Санжар:

    /*Спасибо за Вашу работу!
    Подскажите пожалуйста, а такой вариант приемлем в первом
    задании?*/

    1. Юрий Юрий:

      Пожалуйста 🙂
      Если судить только по результату, то ваш вариант подходит также. Но если с пользовательской точки зрения, то не очень: желательнее всё разбить на функции и описывать действия в программе которые выполняются (например, что значат переменные x, y, z — какие значения пользователю следует вводить).

      1. Аватар Санжар:

        Спасибо за ответ, понял.
        Успехов Вам!

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *

telegram канал
НОВОСТИ RAVESLI