Архив рубрики: Структуры, объединения и перечисления в C++

Сортировка структур. STL Часть 13




сортировка структур с++, stl для начинающихПоказанные в предыдущей части разнообразные сортировки — гибкий и красивый механизм на все случаи жизни. Вот только на практике сортировать в чистом виде последовательности почти никогда не приходится. Это всё из области учебно-показательных задач. На практике куда чаще стоит задача сортировать достаточно объёмные структуры данных (объёмные даже не по своему размеру, а по числу своих полей). И сортировать (или пересортировывать) их предстоит по значениям самых разных полей этих самых структур. Но и здесь на помощь приходят алгоритмы STL, особенно при использовании их совместно с функторами.

Посмотрим типовую модельную задачу, которую мы уже видели раньше — описание учебной группы или факультета:

Программа запрашивает номер поля data, по которому будет вестись сортировка (на самом деле — сравнение). Если этот номер вводится, как положительное число, то сортировка по этому полю идёт в порядке возрастания. Если же номер вводится со знаком минус — то порядок сортировки меняется на обратный:

сортировка структур с++, stl для начинающихсортировка структур с++, stl для начинающих

Обратите внимание, что алгоритму сортировки совершенно безразлично что сортировать: если это числовые данные, то по величине, а если строчные, то в лексографическом порядке.

Рассылка новых уроков по программированию:

Битовые поля в С++




битовые поля с++, структуры в с++ для начинающихВ языке С++ есть возможность задавать элементам структур определённое количество памяти в битах. Например, если необходимо создать структуру данных, соответствующую размеру регистра в каком-либо устройстве. Типом элемента (его называют битовым полем) такой структуры может быть целочисленное (чаще всего типа unsigned) или перечислимое (enum).

Синтаксически битовое поле в структуре определяется следующим образом:
битовые поля с++, структуры в с++ для начинающихНапример:

Мы определили структуру, в которой переменные будут занимать указанное количество бит. 2 + 2 + 4 дает 8 бит (мы выровняли до размера байта). Если в эту структуру дописать еще unsigned short fifth : 5; — уже будет задействовано 2 байта. Во втором байте естественно будет мусор 8 — 5 = 3 бита, которые будут невостребованными.

В отличии от объединений (union) размер битовых полей варьируется, в зависимости от того, сколько бит программист заказал. Если заказано 7 бит (скажем две переменные по 3 бита, и одна — 1 бит), то С++ отведет один байт (8 бит) под эти три переменные. Если программист закажет 11 бит, то С++ отведет два байта (16 бит). Причем во втором байте будут задействованы только 5 бит, а остальные скорее всего будут, как бесполезный хвост. Поэтому при описании битовых полей следует учитывать такое «выравнивание» до байта. Т.е. распределять в нем переменные так, чтоб каждый бит был востребован. Для выравнивания занимаемой памяти можно использовать неименованные битовые поля.

Приведем еще один короткий пример, в котором битовые поля отводятся под дату и время для демонстрации этой технологии.

Результат:

битовые поля с++, структуры в с++ для начинающих

Как видите, битовые поля хранят дату и время. Занимает эта структура 6 байт, хотя для неё хватит и пяти. И на то есть свои причины: сам компилятор может выравнивать отводимую память до четного числа байтов. Например если мы заказали 18 бит, компилятор отведет нам не 3 байта, а 4, учитывая что процессор любит работать с байтами, а не с битами. По крайней мере хранить в памяти или своих регистрах процессор предпочитает не биты, а именно байты. Согласно его разрядности: x32 хранят 4 байта, x64 уже 8 байт. Пусть даже из этих байт работа идет только с одним из них, остальные все равно будут подтягиваться.

Небольшой итог: Битовые поля в структурах обычно используются в низкоуровневом программировании, когда работа идет со значениями, способными занимать не байты, а отдельные биты (ввиду того, что значения небольшие).

Объединения в С++ (union C++)




объединения в с++, union c++, доклад, курсовая работаТехнология объединений union берет свои истоки в 90-х. Слабенькие по нашим временам ЭВМ (сейчас их и компьютерами то не назовешь), мало памяти (все измерялось килобайтами). Жесткие диски по 40 мегабайт были чуть ли не чудом техники, олицетворяющим огроменные объемы информации, которые умельцами «растачивались» специальными ПО до хранения 80 мегабайт (чтоб побольше было). Каждый байт памяти был на вес золота, приходилось экономить на всём. Вот и объединение переменных тоже было призвано (и достаточно успешно, о чем я расскажу ниже) помочь программисту сделать оптимальную и экономную программу, «кушающую» маленькие объемы памяти.

Чтобы понять в чем смысл объединения нужно вспомнить как хранятся переменные. Разные переменные разного типа или одинаковой группы типов (вроде int, long и short) несмотря на работу с одним и тем же типом данных (имею ввиду целое) занимают в памяти разное количество байт. long в любом случае занимает максимальное количество байт в памяти, при этом в память для переменной этого типа вполне можно записать значения int или short. Просто получится, что не все зарезервированные байты long-а будут востребованы. Если поместить к примеру число 325 в long, будут заняты два байта (зависит от разрядности процессора), а остальные байты заполнятся нулями.

Именно в этом и появляется смысл union, ибо эта инструкция говорит компилятору: «Зарезервируй мне место для типа данных с максимальным запросом объема памяти, а я уже буду сама разбираться, как и какие значения в них положить».

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

Доступ к элементам объединения осуществляется так же, как и к элементам структур: Имя объекта объединения myUnion , точка . и имя элемента name1 .

К данным, которые хранят элементы структуры (например short, int, long) мы можем обращаться в любой момент (хоть к одному, хоть и ко всем сразу). А в объединении могут храниться данные либо short, либо int, либо long. Например:

Запускаем программу:

объединения в с++, union c++, доклад, курсовая работа

Как видите, после того, как мы записали значение в элемент name3 типа long int , уже невозможно нормально обращаться к элементу name1 . Все потому, что в их общую память уже записано значение long int, а переменная типа short int неспособна работать с данными такого объема. Схематически можно это представить так:

объединения в с++, union c++, доклад, курсовая работа

Поэтому, чтобы опять работать с данными типа short int необходимо снова присвоить элементу name1 новое значение. Вот и получается — память одна и та же, а переменные в ней размещаются разные. К какой обращаемся — такая и запрашивает из этой памяти значение.

Элементы объединения располагаются в памяти начиная с одного места (как бы накладываются друг на друга), в отличии от элементов структуры (они располагаются в памяти последовательно один за другим).

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

Если бы мы описали просто набор переменных не объединяя их в union, то для их размещения потребовалось бы 2 + 4 + 4 байта = 10 байт. Вот и экономия. А так объединение занимает 4 байта. Целых 6 байт сэкономили, натрамбовав три переменные в один отрезок памяти.

К слову нужно заметить, что эта технология умудрилась получить неплохое продолжение, и в современных языках используется везде где нужно и не нужно. СиШарп, PHP, Делфи, современные наследники Си плюс плюс — все они используют такие объединения. Только в современном мире они уже называются variant. В переменную variant можно без проблем запихнуть до 10 байт (10 это максимум для 32-х разрядных систем под вещественное число). Соответственно в эти 10 байт и все семейство целых и вещественных, и указатели, и объекты, и строки (точнее описатели строк или указатели на них) вмещаются.

На сегодняшний день экономия памяти не так актуальна, как 15-20 лет назад. Конечно, никто не запрещает использовать union, но зачем? Если есть более удобные на сегодня средства работы с памятью для программиста.

Следует отметить также, что программы для выполнения в память загружаются с избытком (т.е. для программы выделяется количество памяти, зачастую чрезвычайно большее чем надо). Это можно наблюдать, написав программу с одной переменной. Казалось бы — переменная 4 байта, но открыв диспетчер задач в Винде программа «почему-то» заняла 10 килобайт. Так работает современная операционная система, выделяя памяти с лихвой. В 90-х, увы, такой роскоши и быть не могло.

В следующем уроке мы рассмотрим битовые поля в С++. Не поленитесь посмотреть видео по теме Объединения (union) в С++:

Бинарные деревья (кратко о главном)




бинарное дерево с++, бинарное дерево c++, иерархия, двоичное деревоВообще, надо признаться, что тема иерархических структур весьма широка. О ней можно спорить много и долго, и так и не прийти к общему знаменателю, четко показывающему что такое иерархия и как с ней работать, по каким канонам в смысле. Я не стану вдаваться в ужасные дебри математического Тартара. Это никому кроме академиков не интересно, и в практическом обозримом слишком тонко.

Итак. В качестве примера дерева возьмем двоичное дерево. Что такое дерево вообще? Это некий набор данных, которые указывают на другие данные. Динамический список. Кстати списки это частный вид дерева. Причем двоичного. Дерево состоит из веток (узлов) и листьев (элементов). Листья — это узлы, которые не указывают ни на кого, у них нет веточек. Бинарное дерево — это дерево, в котором у ветки может быть не более двух листьев или веток. Отсюда и его название — «бинарное» значит «двоичное», т.е. элементов два или меньше. Но никак не более двух.

Вот пример такого бинарного дерева (в картинках Яндекса нашел):

А вот обычное дерево:

У обычного дерева узлов у веток больше может быть.

Не буду вдаваться в классификации деревьев. Это тоже большой объем информации. Об этом можно почитать в Википедии, какие они бывают. Можно набрать в поиске слово «Граф» и почитать. Главное не наткнуться на настоящего графа, у которого бывают графини. По четвергам и субботам :)

Основное правило формирования бинарного дерева в С++: Если значение узла больше добавляемого — добавляется ветка справа, иначе создается ветка слева.

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

Поле Data представляет данные, на основании которых строится дерево. Точнее один из элементов данных. Поля Branch описывают левую и правую ветки дерева, и являются указателями на такую же структуру.

Элементами дерева могут быть любые значения. Массивы, строки (строка это массив символов если что), другие деревья… Полей с данными может быть множество. На каждое поле можно строить свое дерево. Этот пример будет с массивом символов — строкой.

Сразу покажу основной код построения дерева, чтоб можно было понять что за источник данных взят в качестве примера:

Строка. Обычная строка Си, которую будем разводить по веткам. Узлы и листы будут содержать символы, по кодам символов будем же определять вправо или влево строить дерево. Хоть я и взял в строку цифровые символы, это не важно. С таким же успехом можно написать туда строку «Они убили Кенни».

Как должна работать программа? Возьмем символы «12384» из строки. Сначала программа видит , что дерево без корня. Его еще нет. Нужно посеять семя, чтоб оно выросло. Первый символ соответственно станет корнем. Второй символ — «2» будет сравниваться с корнем. По коду его значение больше. Значит у корня должна вырасти ветка вправо (RightBranch) в нашем случае. Далее идет тройка. У нас уже есть две ветки. Программа должна пройтись по ним, проверить: 3 больше 1? — Да. Пойти вправо. Там двойка. 3 больше 2? Так точно. После двойки нет веток, значит нужно создать ветку вправо для узла «2».

Дальше идет восемь. Что с ним делать? То же самое. От единицы пройти через двойку до тройки и так же отрастить ветку право.

После восьмерки идет 4. Это значение пройдет корень. Поскольку оно больше, программа должна посмотреть, есть ли у корня ветка справа. Есть — пойти по ней. Пройти «2» и «3», ибо 4 больше по значению. Дальше справа встретится число 8. Оно меньше. Значит «4» уже пойдет от восьмерки не вправо, а влево.

Простейший код построения дерева (с иерархической структурой) будет выглядеть так:

Обратите внимание: В функции три основных условия:

  1. Если узел-ветка не создана — создать его, наполнить данными и указать NULL (0) для его веток;
  2. Если передаваемые данные больше чем данные текущего узла — попытаться построить его правую ветку, или если она уже есть просто пройти по ней;
  3. То же самое для левой ветки, если данные меньше по значению.

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

Все! А чего вы думали? Древо готово. Иггдрасиль знаменитый вполне мог быть создан вот такими вот 14-ю строками на С++. Или какой там божественно-космический язык применялся? Если вы думаете, что посадить дерево сложно — вот вам очевидное невероятное. Нет. Ничего сложного нет. Ну может быть N-мерное дерево будет чуть более сложнее в реализации…

В данном случае чтоб понять принцип его построения не нужны тонны кода. Не обязательно рассматривать вставку между узлами, чтоб к примеру упорядочить дерево или разрабатывать код для балансировки дерева. Принцип в своей Naked сущности очень прост. И если при написании какого-то паука для серфинга по сайтам в интернете или разных 3D дизайнерских программ типа тех, что разрабатывают Autodesk, знания деревьев и графов нужны сумасшедшие, то в случае простой базовой основы достаточно и чего-то попроще.

Осталось пожалуй для соблюдения «феншуя» написать код обхода дерева и вывода на экран

и освобождения дерева:

Вот здесь пожалуй виден еще один принцип работы с деревьями. В print() рекурсивно вызывается подряд переход по веткам. Причем между вызовами вставлен вывод данных узла, из которого растут ветки, чтоб правильно отобразить на экране или просто отработать скажем при поиске данных.

В освобождении так же происходит рекурсивный вызов освобождения веток. Сначала всю левую, потом всю правую с их подветками и листами, и только потом освобождается сам вызывающий узел. Это важно, поскольку если поставить delete в начале функции получим либо ошибку (что скорее всего) либо все растущие ветки из узла просто не смогут освободиться, зависнув в памяти мусором.

На всякий случай поясню еще один финт в выводе:

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

Для этого определяем глобальную переменную int tabs=0; которая будет считать количество отступов, и по факту (кстати!) являться носителем значения уровня узла, т.е. номером его вложенности. Сколько у этого узла родителей-веток вплоть до корня. Это понятие тоже можно встретить в литературе про деревья. Так что оно там (в коде имеется ввиду) не только для наведения красоты на экране.

Вот полный код (при запуске дерево «растет» не сверху вниз, как на рисунках, а слева направо. Не хотелось усложнять код):

На экране:

бинарное дерево с++, бинарное дерево c++, иерархия, двоичное дерево

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

Видео. Автор — «mycodeschool»:

Перечисления в С++ (enum)





перечисления с++, перечисления c++, enumeration c++, enum c++

Перечисления (enum) используются в C++ для создания констант. Допустим надо объявить константы для музыкальных нот и каждую инициализировать соответствующим порядковым номером. Можно воспользоваться уже знакомым нам способом:

перечисления с++, перечисления c++, enumeration c++, enum c++

Такое объявление занимает много строк кода и не совсем удобно. Используя перечисление, можно определить эти константы иным способом. Синтаксис enum похож на синтаксис структур: ключевое слово — дескриптор — элементы в фигурных скобках через запятую :

перечисления с++, перечисления c++, enumeration c++, enum c++

Это полная форма — с дескриптором (именем перечисления). Как и в случае со структурами, имя перечисления можно не использовать:

перечисления с++, перечисления c++, enumeration c++, enum c++

В первом случае — мы сможем создавать переменные типа перечисления notes. Во втором случае — нет.

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

В консоли отобразится:перечисления с++, перечисления c++, enumeration c++, enum c++

Хоть мы и не присваивали явно значения RE и SOL — они хранят правильные порядковые номера нот.

Имена перечислителей (элементов перечисления) должны быть уникальными. Значения же могут совпадать:

Значение 1 будут хранить KLICHKO_VITALIY и KLICHKO_VLADIMIR. 2 запишется в CHISORA, 3 — в ADAMEK . В элементы перечисления могут быть записаны только целые числа. Изменить ниже в программе значения, которые присвоены элементам в фигурных скобках нельзя. Если создается переменная типа перечисления — она может принять значение только одного из своих элементов:

перечисления с++, перечисления c++, enumeration c++, enum c++

На картинке видно, что записать число в переменную типа champions возможности нет. Можно записать только именованную константу, которая объявлена во время определения enum.

Для закрепления рассмотрим пример. В нём создадим перечисление levelsName. Оно будет содержать шесть элементов — «названия» этажей. Пользователю предложим «покататься» на лифте.

Объявляем целочисленную переменную floor в строке 10 и инициализируем ее именованной константой parking. Так floor примет значение 0. В строке 13 входим в цикл while. Пока пользователь не введет в переменную exitOrNot значение 0, программа будет ему предлагать «кататься на лифте» — выбрать номер этажа. После каждого выбора этажа звучит сигнал (три коротких гудка) и на экран выводится номер этажа и информация о том, что на нём размещено. Это реализовано оператором выбора switch. Его блоки case перебирают элементы перечисления. То есть пользователь вводит целое число, а case ищет, какому из элементов перечисления оно соответствует. Например, если введено 5 — это соответствует значению элемента Restaurant — на экран выводится соответствующее сообщение.

Результат:

перечисления с++, перечисления c++, enumeration c++, enum c++

Посмотрите короткое видео о перечислениях C++. Оно на английском языке, но понять его легко, после прочтения статьи:

Чтобы поддержать наш сайт — нажмите на копилку и выберите любой удобный способ.

Рассылка новых уроков по программированию:


Согласен получать уведомления от purecodecpp.com на мой e-mail

Динамический массив структур C++





динамический массив структур

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

Определение структуры находится в строках 5 — 10. В ней объявлены три элемента name, surname, sum. Ниже объявлены прототипы функций, необходимых для решения задачи. Первая функция Sponsor* AddStruct(Sponsor* Obj, const int amount); будет выделять память для элементов массива структур. Вторая void setData(Sponsor* Obj, const int amount); отвечает за ввод данных в структуру. Третья void showData(const Sponsor* Obj, const int amount); — выводит на экран все данные в виде таблицы. Определения этих функций мы рассмотрим ниже.

Чтобы создать динамический массив структур, надо, как и для создания обычного динамического массива, объявить указатель. Только вместо встроенного типа указать дескриптор структуры — строка 20. Этот указатель пока ни на что не указывает. Можно было бы выделить память под массив структур сразу. Например:

динамический массив структур

Но мы организуем более гибкое выделение памяти под этот динамический массив структур — она будет выделяться по необходимости. Есть один спонсор — выделится память под одну структуру. Есть 3 спонсора — память выделится сначала под одну структуру, потом под вторую и далее под третью. Все будет зависеть от того — решит ли пользователь продолжить ввод.

В строках 21 — 22, объявлены переменные sponsorAmount — счетчик количества спонсоров и YesOrNot — выбор пользователя (продолжить или прервать ввод).

Строки 24 — 34: здесь расположен цикл do while. Он выполняется до тех пор, пока пользователю необходимо вводить данные. В строке 26 вызываем функцию, которая выделяет память под структуру OurSponsors = AddStruct(OurSponsors, sponsorAmount); Опустимся к ее определению в строках 42 — 61. Тут видно, что данная функция будет возвращать указатель на структуру Sponsor. Она принимает два параметра — указатель на структуру и количество структур. Когда она вызывается в первый раз — в нее будет передан объявленный в main указатель OurSponsors и переменная sponsorAmount, которая равна нулю. В функции выполнится блок if — выделится память для одной структуры (строки 44 — 47). Потом функция вернет адрес (указатель) на этот участок памяти и он будет записан в OurSponsors — строка 26.

В строке 27 вызываем функцию, которая позволит внести данные в структуру. Её определение находится в строках 63 — 73. После внесения данных, переменная sponsorAmount увеличивается на единицу. Пользователю предлагаем сделать выбор — продолжить ввод или завершить работу. Если продолжаем — снова вызывается функция AddStruct().




Надо помнить, что указатель OurSponsors уже ссылается на участок памяти с записанными данными. Поэтому не получится просто перевыделить память. Сначала необходимо позаботиться о сохранении данных. Посмотрите на блок else строки 48 — 59. В строке 50 создаем временный указатель. Под него выделяем память для amount + 1 структур (т.е. на одну структуру больше, чем приняла функция). Далее копируем данные из принятого объекта. Последний объект массива структур tempObj останется незаполненным. Когда данные скопированы, освобождаем память Obj — строка 57 и записываем в этот указатель новый адрес. Теперь он будет указывать на память, в которой есть сохраненные данные и дополнительный выделенный участок памяти для заполнения новыми данными.

И снова вызывается функция setData(), которая позволит внести данные в новый выделенный участок памяти — в последний элемент массива структур.

Когда пользователь решит больше не вводить данные — он нажимает ноль. После этого сработает функция showData() и на экране отобразится таблица с данными со всех структур динамического массива. В самом конце программы не забываем освободить память, которую занимает динамический массив структур.

Результат — ввод данных:

с++ динамический массив структур

Таблица:

с++ динамический массив структур

Чтобы поддержать наш сайт — нажмите на копилку и выберите любой удобный способ.

Рассылка новых уроков по программированию:


Согласен получать уведомления от purecodecpp.com на мой e-mail

Массив структур. Указатель на структуру




массив структур с++, массив структур c++

Точно так же, как мы можем создавать массивы любых встроенных типов данных (int, char…), можно создавать массивы структур. Рассмотрим это на примере со структурой WonderfulWoman знакомой нам из первой и второй частей статьи о структурах C++.

Надо внести данные о 7-ми моделях. Данные показать на экран в виде таблицы, чтобы было легче их сравнить. Схематично так:

массив структур с++, массив структур c++, указатель на структуру c++

Чтобы это реализовать — набирайте код:

Массив объектов структуры WonderfulWoman объявлен в строке 29 и в этой же строке все элементы объектов в этом массиве инициализированы нулями. Как видите — кроме того, что вместо встроенного типа мы написали дескриптор структуры, больше ничего нового в этом объявлении для нас нет. Дали имя массиву, указали количество элементов (объектов), инициализировали нулями — всё. Таким образом мы создали массив объектов структуры.

Как обратиться к элементу конкретного объекта структуры? Обычное обращение через точку уже не подойдет:

массив структур с++, массив структур c++, указатель на структуру c++

Конечно, ведь непонятно в какой именно объект из 7 надо внести данные. Так как каждый объект теперь является ячейкой массива структур, чтобы обратиться к его элементам надо сначала указать индекс объекта в квадратных скобках. Только потом точку и имя элемента:

массив структур с++, массив структур c++, указатель на структуру c++

В строках 31- 49 находится цикл for. С его помощью вносятся данные во все объекты массива структур. Обратите внимание на строку 47. Без этого оператора cin.get(); вы столкнетесь с ошибкой, о которой компилятор вам не сообщит. Она заключается в том, что после внесения данных в элемент Woman[i].volume.hips и нажатия Enter, в потоке ввода остаётся символ конца строки. Когда цикл продолжит работу — этот символ автоматически запишется в Woman[i].name следующего объекта и нам сразу будет предложено ввести возраст. То есть имя внести мы не сможем. Чтобы этого избежать, надо считать из потока этот символ с помощью cin.get(); .

Результат — заполняем структуры данными :

массив структур с++, массив структур c++, указатель на структуру c++

После ввода данных всех моделей, экран очистится (за что отвечает system(«cls»);) и мы увидим таблицу:

массив структур с++, массив структур c++, указатель на структуру c++


В этой статье поговорим ещё об указателях на структуры. Если, например, надо написать функцию, в которой будут записываться данные в элементы структуры, то необходимо передать эту структуру по указателю. Иначе изменения не сохранятся. Рассмотрим пример со структурой Size из предыдущего листинга. Надо написать функцию, в которой пользователь вносит данные в элементы структуры.

Объявляя указатель на структуру, не забывайте о том, что надо выделить память под неё — строка 18. Указатель объявлен. Давайте разберёмся теперь, как обращаться к элементам структуры, через указатель. Допустим надо внести значение в элемент waist. Использовать . — операцию доступа по объекту уже не получится:

массив структур с++, массив структур c++, указатель на структуру c++Ошибку подсвечивает потому, что указатель должен хранить адрес, а он пытается обратиться к элементу типа int. Попробуем разыменовать указатель на структуру:

массив структур с++, массив структур c++, указатель на структуру c++

Теперь все нормально, но это не совсем удобная запись. В С++ есть специальная операция доступа по указателю (стрелочка — состоит из тире и знака больше) -> . Она как бы показывает, на какой элемент структуры ссылается указатель:

массив структур с++, массив структур c++, указатель на структуру c++

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

Результат:

массив структур с++, массив структур c++, указатель на структуру c++

Чтобы поддержать наш сайт — нажмите на копилку и выберите любой удобный способ.

Рассылка новых уроков по С++


Согласен получать уведомления от purecodecpp.com на мой e-mail

Структуры в C++. Часть 2





структуры с++, структуры c++, struct c++. вложенные структуры

Со структурами C++ мы предварительно познакомились в предыдущей статье. Продолжаем знакомство.

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

Инициализация. Элементы структуры можно инициализировать сразу при объявлении объекта.

Объявляем объект volume1 в строке 13 и проводим его инициализацию. Похоже на инициализацию элементов массива — в фигурных скобках и через запятую. Эти данные запишутся в соответствующие элементы структуры по порядку. В каком порядке определены элементы в структуре — в таком и произойдет запись значений. Операцию равно можно не использовать (согласно C++11).

Рассмотрим такую инициализацию на примере с более сложной структурой WonderfulWoman :

структуры с++, структуры c++, struct c++. вложенные структуры

Объект этой структуры можно инициализировать так:

структуры с++, структуры c++, struct c++. вложенные структуры

Но если структура содержит больше двух-трех элементов -желательно так не делать. Это немного запутывает и усложняет читаемость и понимание программы.

Объявление объектов. Создать объект структуры можно сразу во время её определения. Для этого необходимо дать имя объекту между точкой с запятой и закрывающей фигурной скобкой:

В С++ есть возможность определить структуру без дескриптора (без имени типа):

В таком случае надо обязательно объявлять объекты в определении структуры. В главной функции вы уже не сможете создать другие объекты этой структуры, так как дескриптора нет. Этот прием есть смысл применять, если объектов структуры будет очень мало — 1 или 2.

Присваивание (=) для структур. Для объектов одной структуры можно применить операцию присваивания = . Операция присвоит элементам одного объекта значения элементов второго объекта.

Это так называемое поэлементное или почленное присваивание. На экране:

структуры с++, структуры c++, struct c++. вложенные структуры

Сработало — все элементы объекта структуры volume2 стали равны элементам объекта volume1.

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

На экране:

структуры с++, структуры c++, struct c++. вложенные структуры

sizeof показал нам сколько памяти занимает каждый элемент структуры HandsomeMan. Мы эти значения суммировали и получили 25. То есть по идее структура должна занимать 25 байт оперативной памяти. Но когда мы применяем оператор sizeof к структуре — получаем значение 28.

Размер объекта структуры не всегда равен сумме размеров его элементов. Так случается из-за выравнивания элементов разной длины. Поэтому в структуре могут встречаться безымянные участки памяти. Если вам надо знать сколько в действительности занимает структура в памяти — примените sizeof , как в примере.

Это видео из предыдущего урока. Кто не смотрел — уделите время




Мы узнали как определять структуры, как объявлять и инициализировать их объекты. Если нам понадобится создать больше чем 2-3 объекта структуры, тогда лучше создать массив структур. Эту тему рассмотрим в следующей статье.

Чтобы поддержать наш сайт — нажмите на копилку и выберите любой удобный способ.

Рассылка новых уроков:


Согласен получать уведомления от purecodecpp.com на мой e-mail

Структуры в C++. Часть 1




структуры с++, структуры c++Изучив структуры C++ в этом уроке, перед вами по настоящему приоткроется пространство для творчества в программировании. До этого урока, мы могли использовать стандартные (встроенные) типы данных для переменных в своих программах (int, float, char, bool). Сейчас же мы научимся самостоятельно создавать что-то вроде своего собственного типа данных. Это делается при помощи структур.

Как всегда, в начале статьи разберемся зачем нам это может понадобиться. Допустим, необходимо сохранить данные о девушке модели. Нам надо хранить о ней такие данные: имя, возраст, рост, вес, объёмы и знание/незнание английского языка. Как видите, все данные о модели будут отличаться типами: имя надо хранить в массиве типа char, объемы — в переменных типа int, для отметки о владении английским подойдет переменная типа bool… Получается, что для хранения этих данных, массив мы никак не сможем использовать, так как он хранит однотипные данные. Используя структуры появляется возможность сгруппировать все эти данные, условно объединенные логической связью, в одно целое. Смотрим:

структуры с++, структуры c++, struct c++

Для определения структуры используется ключевое слово struct. После него указывается дескриптор (имя нового типа данных). Имя дает программист. Оно подчиняется тем же правилам, что и имена переменных. У нас дескриптор структуры — это WonderfulWoman. В фигурных скобках размещаются элементы (члены) структуры — именованные переменные или массивы любого типа данных С++. Структура может хранить элементы разных типов. Как видите в нашей структуре есть элементы типа int, char и bool. Завершается определение структуры точкой с запятой ;

После того, как структура определена, мы можем создавать переменные типа структуры — объекты структуры. Создаются они точно так же, как и переменные типа bool, int, char… Пишем дескриптор («тип») и даем имя объекту структуры. Например в main-функции создаем переменную
WonderfulWoman firstWoman; Здесь WonderfulWoman будет играть роль типа, а firstWoman — роль объявленной переменной.

Элементом структуры также может быть объект другой, определённой ранее, структуры. То есть структуры можно вкладывать одну в другую. Рассмотрим пример. В нём также разберемся, как обращаться к элементам структуры, чтобы записать в них данные, например, и затем показывать на экран.

Идем по порядку. Строки 4 — 9: определение структуры Size. Она содержит три элемента типа int, которые будут хранить замеры модели — объемы груди, талии, бёдер. Эту структуру мы вложим в расположенную под ней структуру — WonderfulWoman. Она содержит элементы разных типов — символьный массив, переменные типа int, bool. В строке 17 объявлен объект volume типа Size. Это и есть вложение структуры. В 21-й строке находится прототип функции, которая будет отображать данные структуры WonderfulWoman на экран. Об этой функции поговорим ниже, когда дойдем до её определения.

Строка 27 — объявление объекта структуры: WonderfulWoman firstWoman = {}; Тут понятно: WonderfulWoman — дескриптор (тип), firstWoman — объект структуры. Такая запись = {}; установит все значения элементов структуры WonderfulWoman в ноль. В том числе, всем элементам символьного массива будет присвоено значение \0 . Так как объект volume является элементом структуры WonderfulWoman, его элементам так же будут присвоены нули. То есть такая запись позволит очистить все элементы структуры от мусора (остаточных данных от других программ).

Обращение к элементам структуры. Чтобы записать или извлечь данные нам надо обратиться к конкретному элементу объекта структуры. Для этого применяется операция принадлежности . (точка): firstWoman.age = 23; Надо нам внести данные о возрасте — пишем имя объекта структуры, за ним . (точка) и далее имя элемента структуры, в котором должно храниться количество лет модели. Чтобы сохранить имя — используем библиотечную функцию strcpy_s() или strcpy() (в зависимости от вашей среды разработки).

Внимательно посмотрите на строки 36 — 38. Там мы присваиваем значения элементам вложенной структуры. Например: firstWoman.volume.breast = 90; Как и раньше пишем имя объекта структуры, за ним . (точка). Далее имя объекта вложенной структуры, снова . (точка). И только теперь выбираем элемент вложенной структуры breast, к которому надо обратиться.

Последнее — структуру свободно можно передавать в функцию и возвращать из функции, как и обычные переменные. Определение функции для вывода данных структуры на экран — в строках 45 — 54. Так как нам надо только показать данные и очень желательно их не испортить в функции, передаем структуру с использованием оператора const. Вызов функции находится в строке 40. Тут просто передаем объект структуры в функцию, указав его имя. Результат:

структуры с++, структуры c++, struct c++

Домашнее задание для вас: написать еще одну функцию для нашей программы, в которой пользователю предлагается вводить данные о модели с клавиатуры. Внести данные надо будет о трёх моделях и показать их на экран.

В этом видео рассказано о структурах с 4-й минуты:




В этой статье (первой части) мы очень поверхностно рассмотрели структуры C++. В дополнительной статье (во второй части) о структурах еще поговорим об особенностях определения структуры, об инициализации объектов структуры, о присваивании структур с использованием операции = и о том сколько памяти выделяется под объекты структур.

Чтобы поддержать наш сайт — нажмите на копилку и выберите любой удобный способ.

Рассылка новых уроков:


Согласен получать уведомления от purecodecpp.com на мой e-mail