Как инициализировать структуру в си

от admin

Структуры

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

Определение структуры

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

Имя_структуры представляет произвольный идентификатор, к которому применяются те же правила, что и при наименовании переменных.

После имени структуры в фигурных скобках помещаются компоненты структуры — объекты, которые составляют структуру.

Следует отметить, что в отличие от функции при определении структуры после закрывающей фигурной скобки идет точка с запятой.

Например, определим простейшую структуру:

Здесь определена структура person , которая имеет два элемента: age (представляет тип int ) и name (представляет указатель на тип char ).

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

Использование структуры

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

Здесь определена переменная tom, которая представляет структуру person . И при каждом определении переменной типа структуры ей будет выделяться память, необходимая для хранения ее элементов.

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

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

По позиции : значения передаются элементам структуры в том порядке, в котором они следуют в структуре:

Так как в структуре person первым определено свойство age, которое представляет тип int — число, то в фигурных скобках вначале идет число, которое передается элементу age . Вторым идет элемент name , который представляет указатель на тип char или строку, соответственно вторым идет строка. И так далее для всех элементов структуры по порядку.

По имени : значения передаются элементам структуры по имени, независимо от порядка:

В этом случае перед именем элемента указывается точка, например, .name .

Обращение к элементам структуры

Также после создания переменной структуры можно обращаться к ее элементам — получать их значения или, наоборот, присваивать им новые значения. Для обращения к элементам структуры используется операция «точка»:

Теперь объединим все вместе в рамках программы:

Консольный вывод программы:

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

Объединение определение структуры и ее переменных.

Мы можем одновременно совмещать определение типа структуры и ее переменных:

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

Можно тут же инициализировать структуру:

Можно определить сразу несколько переменных:

При подобном определении мы можем даже не указывать имя структуры:

В этом случае компилятор все равно будет знать, что переменная tom представляет структуры с двумя элементами name и age. И соответственно мы также с этими переменными сможем работать. Другое дело, что мы не сможем задать новые переменные этой структуры в других местах программы.

typedef

Еще один способ определения структуры представляет ключевое слово typedef :

В конце определения структуры после закрывающей фигурной скобки идет ее обозначение — в данном случае person . В дальнейшем мы можем использовать это обозначение для создания переменной структуры. При этом в отличие от примеров выше здесь при определении переменной не надо использовать слово struct .

Директива define

Еще один способ определить структуру представляет применение препроцессорной директивы #define :

В данном случае директива define определяет константу PERSON, вместо которой при обработке исходного кода препроцессором будет вставляться код структуры struct

Копирование структур

Одну структуру можно присвавивать другой структуре того же типа. При копировании элементы структуры получают копии значений:

Здесь в переменную bob копируются данные из структуры tom . Далее мы для структуры bob меняется значение поля name . В итоге мы получим следующий консольный вывод:

Ввод с консоли данных для структуры

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

структуры в C

Это совокупность нескольких переменных, часто различных типов, сгруппированных под единым именем для удобства обращения. Главное изменение, внесённое стандартом ANSI в работу со структурами, — это определение присваивания структур. Теперь структуры можно копировать, присваивать, передавать в функции и возвращать из функций.

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

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

Объявление со словом struct фактически вводит новый тип данных. Поэтому допустимо такое объявление переменных:

Данный код объявляет переменные \(x, y, z\) определённого именованного типа и выделяет для них место в памяти. Объявление структуры, после которого нет списка переменных, не выделяет никакой памяти для объектов, а просто описывает форму структуры. Так же можно объявлять переменные, используя метку структуры:

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

Обращение к элементам структуры выполняется следующим образом:

Структуры можно вкладывать друг в друга:

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

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

Вместо того чтобы помещать результат во временную переменную, мы инкрементировали компоненты структуры p1, чтобы подчеркнуть тот факт, что параметры-структуры передаются по значениям, как и любые другие параметры.

Если в функцию необходимо передать большую структуру, это лучше сделать передав указатель на неё, а не копию всех её данных. Указатели на структуры обладают всеми свойстваи указателей на обычные переменные.

Указатели на структуры используются так часто, что для удобства записи ссылок по ним введено дополнительное обозначение: p->элемент-структуры

Структуры

Мир вокруг можно моделировать различными способами. Самым естественным из них является представление о нём, как о наборе объектов. У каждого объекта есть свои свойства. Например, для человека это возраст, пол, рост, вес и т.д. Для велосипеда – тип, размер колёс, вес, материал, изготовитель и пр. Для товара в магазине – идентификационный номер, название, группа, вес, цена, скидка и т.д.

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

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

Объявление структуры

Синтаксис объявления структуры

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

В том случае, если несколько полей имеют один тип, то их можно перечислить через запятую:

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

Структура, объявленная в глобальном контексте, видна всем. Структура также может быть объявлена внутри функции:

Можно упростить пример: синтаксис языка позволяет создавать экземпляры структуры сразу же после определения:

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

В этом примере мы создали переменную A. Она является структурой с двумя полями.

Начальная инициализация структур

Структуру можно инициализировать во время создания как массив. Поля в этом случае будут присваиваться по порядку.

Замечание: таким образом можно только иницализировать структуру. Присваивать значение всей структуре таким образом нельзя.

Современный стандарт си позволяет инициализировать поля структуры по имени. Для этого используется следующий синтакис:

Определение нового типа

Когда мы определяем новую структуру с помощью служебного слова struct, в пространстве имён структур (оно не имеет ничего общего с пространствами имён С++) создаётся новый идентификатор. Для доступа к нему необходимо использовать служебное слово struct. Можно определить новый тип с помощью служебного слова typedef. Тогда будет создан псевдоним для нашей структуры, видимый в глобальном контексте.

Теперь при работе с типом Point нет необходимости каждый раз писать слово struct. Два объявления можно объединить в одно

Замечание. Если мы создаём новый тип-структуру, полем которого является указатель на этот же тип, то его необходимо объявлять явно с использованием служебного слова struct

Указатели на структуру

Указатель на структуру создаётся как обычно. Отличие заключается в том, что можно обращаться к полям структуры через указатель с помощью операции «стрелка» (минус + больше). Пример – пользователь вводит число – размер массива пользователей. Поле этого вводит для каждого из них логин и пароль. Третье поле — идентификатор – задаётся автоматически. После этого все пользователи выводятся на экран.

Читать:
Как вызвать функцию из dll

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

Устройство структуры в памяти

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

Первая структура должна иметь размер 6 байт, вторая 8 байт, третья 7 байт, однако на 32-разрядной машине компилятор VC сделает их все три равными 8 байт. Стандарт гарантирует, что поля расположены друг за другом, но не гарантирует, что непрерывно.

Есть возможность изменить упаковку структур в памяти. Можно явно указать компилятору каким образом производить упаковку полей структуры, объединений или полей класса. Каким образом это делать, зависит от компилятора. Один из самых распространённых способов прагма pack()

У неё есть несколько разновидностей, рассмотрим только одну. pragma pack(n) указывает значение в байтах, используемое для упаковки. Если параметр компилятора не заданы для модуля значения по умолчанию n 8. Допустимыми значениями являются 1, 2, 4, 8 и 16. Выравнивание поля происходит по адресу, кратному n или сумме нескольких полей объекта, в зависимости от того, какая из этих величин меньше.

Использование #pragma pack не приветствуется: логика работы программы не должна зависить от внутреннего представления структуры (если, конечно, вы не занимаетесь системным программированием или ломаете чужие программы и сети).

Приведение типов

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

Этот пример работает, но это хак, которого необходимо избегать. Правильно писать так

Привести массив к структуре (или любому другому типу) по стандарту также невозможно (хотя в различных компиляторах есть для этого инструменты).
Но в си возможно всё.

Но запомните, что в данном случае поведение не определено.

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

Структура сама может являться полем структуры. Пример: структура Model – модель автомобиля, имеет название, номер, год выпуска и поле Make, которое в свою очередь хранит номер марки и её название.

Вложенные структуры инициализируются как многомерные массивы. В предыдущем примере можно произвести начальную инициализацию следующим образом:

P.S. подобным образом инициализировать строки не стоит, здесь так сделано только для того, чтобы упростить код.

Указатели на поля структуры и на вложенные структуры

Указатели на поля структуры определяются также, как и обычные указатели. Указатели на вложенные структуры возможны только тогда, когда структура определена. Немного переделаем предыдущий пример: «деанонимизируем» вложенную безымянную структуру и возьмём указатели на поля структуры Model:

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

Примеры

1. Стек, реализованный с помощью структуры «Узел», которая хранит значение (в нашем примере типа int) и указатель на следующий узел. Это неэффективная реализация, которая требует удаления и выделения памяти под узел при каждом вызове операции push и pop.

2. Реализуем структуру — массив, и некоторые операции для работы с массивами. Тип массива зададим макроподстановкой.

3. Структура Линия, состоит из двух структур точек. Для краткости реализуем только пару операций

Обратите внимание на операции создания и копирования линии. Обязательно нужно копировать содержимое, иначе при изменении или удалении объектов, которые мы получили в качестве аргументов, наша линия также изменится. Если структура содержит другие структуры в качестве полей, то необходимо проводить копирование содержимого всех полей. Глубокое копирование позволяет избежать неявных зависимостей.

4. Структура комплексное число и функции для работы с ней.

5. Биномиальная куча и сортировка на куче

Всё ещё не понятно? – пиши вопросы на ящик

Name already in use

school_notes / struct / STRUCT.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

Структура — это абстракция над переменными. Условный «мешок» в котором может храниться несколько переменных. Как и у обычной переменной у структуры есть свой тип и имя.

Как создать структуру?

Поскольку Си не знает заранее, какие структуры мы хотим использовать, то нам нужно их определить.

Теперь компилятор в Си знает, что есть тип данных struct s_gnl_struct и поэтому его можно использовать.

В примере мы создали «мешок с переменными» который имеет тип struct s_gnl_struct и имя a Обращения к содержимому «мешка» происходит через «.»

Расположение в памяти

Любая переменная в вашей программе занимает какое-то место в памяти. Например int — 4 байта, size_t — 8, char — 1 и т.д.

Сколько места в памяти занимает структура?

Очевидно, что как минимум столько, сколько занимают все переменные из структуры. Для нашего примера int + int + char = 4 + 4 + 1, то есть как минимум 9 байт (на самом деле 12, ниже показано почему так происходит и как это выглядит).

Больше примеров

На картинке показано как хранятся отдельные переменные в памяти компьютера. Оранжевый — адреса ячеек памяти; синий — размер, который занимает конкретный тип; зелёный — количество байт, которые заняты, белый — рандомная память.

int, size_t, char где-то в памяти компьютера:

В отличие от обычных переменных тип структуры целостный, и для быстрого доступа к полям структуры происходит т.н. выравнивание полей (для ускорения вычислений). К полям структуры могут быть добавленны пустые байты, чтобы итоговый размер структуры был кратен размеру типа, который имеет наибольший размер. Непонятно сказанно, но на примере к типу char добавляется 3 байта, чтобы его размер стал кратен 4 (т.е. размеру int).

Так выглядит s_gnl_struct где-то в памяти:

Причина, почему под char выделяется 4 байта (ТАК БЫВАЕТ НЕ ВСЕГДА) — тема отдельного топика. Можно погуглить padding in memory, например вот хорошая статья про выравнивание структур в Си

P.S. Если мы добавим в структуру еще один char в конце размер структуры все еще останется 12. А если мы добавим этот char в начало то размер структуры станет 16. Подробнее про это в статье выше.

typedef — это просто слово для объявления пользовательского (то есть нашего собственного) типа. Чтобы при объявлении структур не писать много раз

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

теперь вместо огромного и плохо читаемого struct s_gnl_struct other; можно писать t_gnl other;

Напоминалка про динамическое выделение

Как и все другие переменные структуру можно просто создать (в стековой памяти):

Эта структура просто является переменной и к этой переменной нужно обращаться через .

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

Здесь мы выделили динамическую память, чтобы в ней лежала наша структура, и присвоили указатель на эту память (то что вернул malloc() ) в переменную указателя на структуру.

P.S. Если забыть поставить \* , то вместо N байт, которые нужны нам для структуры выделится 8 байт ( размер указателя ). Эту ошибка приводит к трудно воспроизводимым segmentation fault, и долгим часам дебага программы на Си.

P.S.S. Я часто вижу таккую запись

По логике она ничем не отличается от примера выше. Здесь всё еще есть риск поставить лишнюю * и пострадать от сегфолтов

И некоторые программисты считают, что запись из первого примера читается лучше (нет лишнего приведения типов, и не указываются избыточные скобочки в sizeof) по мне так это дело вкуса, подробнее про оформление маллока тут

Обращаться к полям структуры можно все ещё через . , но необходимо написать много лишних символов. * для разыменования указателя, ( ) для указания приоритета операций, и только потом . ! И всё это слева и справа от имени указателя на структуру.

Для упрощения жизни придумали оператор -> который позволяет через указатель на структуру обратиться к её полям.

Еще раз: если у вас есть указатель на структуру к ее полям можно обращаться через -> . Если у вас есть просто структура то к её полям нельзя обращаться через -> (что в принцепе бессмысленно), к её полям нужно обращаться через .

В структуре можно дать имена каждому биту какого-либо типа. Смотрим код:

Данная структура займёт в памяти 4 байта (!).

Работает это следующим образом. Мы создаём структуру, в которой хранится тип unsigned int , размер этого типа — 4 байта или 32 бита. Полсе : мы указываем, сколько бит от этого типа будет занимать поле.

При объявлении структуры как в коде выше поля b1, b2, b3, b4 будут иметь размер 1 БИТ, а f1, f2 соответственно по 10 бит. Эта штука может быть очень полезна, если вы хотите экономно хранить множество каких-либо флагов и иметь очень удобное к ним обращение.

Пример для флагов в ft_printf

размер структуры flags — 1 байт!

Структуры хорошо использовать там, где требуется хранить много связанных по смыслу переменных в одном месте. Относительно школьных проектов, например — это значения спецификаторов формата в ft_printf, структура хранящая информацию о последнем считывании в get_next_line, или структура структур, которая хранит всю информацию об игре cub3D.

Похожие статьи