Как создать массив структур в си
Из структур можно создавать массивы также, как массивы других типов. И все форматы определения массива структур будут аналогичны определению массивов других типов:
В данном случае определен массив структур person из 10 элементов.
Используем массив структур в программе:
В массиве people определено 4 объекта person. При инициализации данные каждой отдельной структуры заключаются во вложенные фигурные скобки:
Обращение к элементам массива структур происходит по индексу people[0] . А чтобы обратиться к элементу структуры из массива, после индекса указывается имя элемента структуры: people[i].name
Консольный вывод программы:
И также как с массивами других типов с массивами структур можно использовать указатели:
Здесь в массиве people те же 4 элемента person. Для их просмотра создан указатель *p, который устанавливается на начало массива people. И в цикле получаем элементы структур через этот указатель. После завершения каждой итерации указатель увеличивается на единицу, то есть перемещается в памяти на количество байт, которые занимает одна структура. И эти действия продолжаются пока указатель не дойдет до конца массива, который можно получить через выражение people+n.
Структуры в Си: конструктор, массивы и указатель на структуру
Структуры в Си очень гибкие, поэтому их можно применять в разных местах. Например , можно создать массив структур. Причем создание массива из структур ничем не отличается от создания массива с другими типами данных. Например:
struct worker staff_member[15];
То ест ь м ы определили массив структур « worker » из 15 компоненто в « staff_m e mber » .
Предлагаем посмотреть массив структур в небольшой программке:
#include <stdio.h>
struct worker
<
int age;
char firstname[30];
>;
int main(void)
<
struct worker staff_member[] = <28, "Тихон", 34, "Дормидонт", 29, "Владлен", 48, "Анфиса">;
int n = sizeof(staff_member)/sizeof(staff_member[0]);
for(int i=0; i<n; i++)
<
printf(«Имя:%s \t Возраст: %d \n», staff_member[i].firstname, staff_member[i].age);
>
return 0;
>
Важно отметить, что у нас в массиве создано 4 объекта « staff_member » . У каждого объекта по 2 элемента, поэтому при инициализации будет передано 8 значений. Но так как обращение идет к структуре, значит , будет определено, что в массиве структур С будет всего 4 объекта « staff_member » , а не 8.
Массив структур в Си позволяет использовать указатель на структуру. Например:
struct worker
<
int age;
char firstname[30];
>;
int main(void)
<
struct worker staff_memeber[] = <28, "Тихон", 34, "Дормидонт", 29, "Владлен", 48, "Анфиса">;
int n = sizeof(staff_memeber)/sizeof(staff_memeber[0]);
for(struct worker *s=staff_memeber; s < staff_memeber+n; s++)
<
printf(«Имя:%s \t Возраст: %d \n», s->firstname, s->age);
>
return 0;
>
В массиве « worker » расположены все те же объекты, однако для их просмотра мы создали указатель « *s » .
Заключение
Структуры в Си используются для того, чтобы удобнее было оперировать разными типами данных одного объекта. С каждой отдельной структурой можно работать как с определенным типом данных. Например, как мы показали в статье, можно создать массив структур в С , а далее работать со структурами как с обычными компонентами массива.
Мы будем очень благодарны
если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.
How do you make an array of structs in C?
I’m trying to make an array of structs where each struct represents a celestial body.
I don’t have that much experience with structs, which is why I decided to try to use them instead of a whole bunch of arrays. However, I keep on running into numerous different errors. I’ve tried to implement the techniques that I’ve seen on various threads and on StackOverflow (such as Array of structs in C and C — initialize array of structs), however not all of them were applicable.
Further information for those who have read this far: I don’t need any of this to be dynamic, I know/define the size of everything beforehand. I also need this to be a global array as I’m accessing this in several different methods which have defined arguments (i.e. GLUT methods).
This is how I’m defining the struct in my header:
I have a list of other global variables that I’m defining before I define the interior of the struct, and one of those is the array of this struct (basically, if I’m being too unclear in my fogged speak, the line below is above the stuff above):
Just so you know, n is something that I’ve legitimately defined (i.e. #define n 1 ).
I use this array in several different methods, but the easiest and least space consuming one is a simplified form of my main. Here I initialize all of the variables in each of the structs, just to set the variables for certain before I modify them in some way:
The current error that I’m facing is nbody.c:32:13: error: array type has incomplete element type where line 32 is where I’m making the array of the structs.
One last clarification, by header I mean the space above int main(void) but in the same *.c file.
Name already in use
C / Lecture 3 / Lecture_3.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
Темы, которые будут рассмотрены в этой лекции:
- Тип данных структура;
- Тип данных объединение;
- Подробнее о препроцессоре и его директивы;
- Операция typedef;
- Динамическое распределение памяти;
- Пример использования динамического распределения памяти;
- Использование структур и динамически выделенной памяти для реализации структур данных. Стек, деревья и другие.
В языке C существует способ построения сложных типов данных путем объединения уже существующих типов в структуры. Описание структуры имеет вид:
Переменная, фугурирующая в описании типа структуры называется полем структуры (компонентой структуры). Такое описание вводит новый тип данных, именуемый:
Таким образом, впоследствии можно будет описывать переменные с введенным типом:
Можно объединить описание структуры с описанием переменной:
Если структура используется для описания только нескольких переменных в одном месте программы, то можно опустить имя_типа_структуры:
При этом описание переменных этого же типа в другом месте программы будет невозможно.
Переменную типа структуры можно рассматривать как объединение полей структуры под одним именем. Значение переменной типа структуры распадается на значения входящих в нее полей. Для доступа к полю переменной используется следующая запись:
Примечание: Иногда структуры называют неоднородными массивами (в отличие от обычных массивов, которые являются однородными).
Переменные типа структур можно присваивать друг другу если они относятся к одному и тому же типу.
При таком присваивании все поля одной структуры будут скопированы в поля другой структуры.
Указатели на структуры
Структура располагается в памяти в последовательных ячейках. Размер памяти занимаемый структурой является суммой размеров входящих в нее полей (возможно с учетом выравнивания некоторых на границу слова).

Описание указателя на структуру имеет вид:
Если p — указатель на структуру, то ссылка на поле структуры имеет вид:
Передача структур в качестве параметра
Передача структур в качестве параметров функций обычно происходит через указатели на структуры.
В качестве полей структур могут быть использованы другие структуры (такие структуры называются вложенными). Такие структуры должны быть определены до содержащей их структуры.
Описание массива структур аналогично описанию массива любого другого типа.
Обработка элементов массива структур производится аналогично обработке элементов массива любого другого типа.
Объединение — это средство, позволяющее запоминать данные различных типов в одном и том же месте памяти.
Объединения описываются так же, как и структуры, за исключением того, что вместо ключевого слова struct используется ключевое слово union.
Доступ к полям объединения осуществляется аналогично доступу к полям структуры.
Схема распределения памяти:

Использование объединений помогает создавать машинно-независимый (переносимый) код. Поскольку компилятор отслеживает настоящие размеры переменных, образующих объединение, уменьшается зависимость от компьютера. Не нужно беспокоиться о размере целых или вещественных чисел, символов или чего-либо еще.
Объединения часто используются при необходимости преобразования типов, поскольку можно обращаться к данным, хранящимся в объединении, совершенно различными способами.
Препроцессор выполняет предварительную обработку исходного текста на языке C, осуществляя в нем замены в соответствии со специальными директивами. После работы препроцессора начинает работу собственно компилятор, который уже переводит программу в машинный код.
Директивы препроцессора располагаются в исходном тексте программы и начинаются с символа #.
Ранее мы уже не раз использовали директиву #include. Теперь рассмотрим ее подробнее
Формат директивы #include:
Когда в тексте программы встречается директива #include, препроцессор ищет указанный за ней файл и включает его содержимое вместо этой директивы. Файл может указываться с помощью абсолютного или относительного пути.
Файл, включаемый директивой #include может содержать пройзвольный текст на языке C. Однако, обычно он содержит заголовки функций, описание констант внешних переменных и так далее (заголовочные файлы, см. предыдущую лекцию).
Если имя файла заключено в угловые скобки, то считается, что файл расположен в одном из стандартных каталогов. Обычно в этих каталогах хранятся заголовочные файлы, поставляемые с компилятором.
Если имя файла указано в кавычках, то поиск файла начинается с текущего каталога, а затем продолжается в стандартных каталогах.
Формат директивы #define:
макроопределение — имя, составленное по правилам построения имен;
строка_замещения — произвольная текстовая строка.
Директива #define указывает препроцессору заменять все вхождения макроопределения в тексте программы на его строку замещения. Процесс замещения называется макрорасширением.
Макроопределение действует с момента появления директивы #define и до конца файла. Макрорасширения не происходит, если макроопределение встречается в текстовой константе.
Макроопределение может содержать параметры, и тогда оно имеет вид:
параметры — это список имен, разделенных запятыми (может быть пустым).
Теперь при использовании макроопределения SQUARE должен быть указан фактический параметр:
Препроцессор сначала заменит в строке замещения все вхождения формального параметра X на фактический параметр, а затем заменит макроопределение на полученную строку замещения.
Таким образом будет получен текст:
Макроопределение может содержать несколько параметров.
Операция определения типа typedef
Операция определения типа typedef позволяет давать новые имена типам данных, допустимых в языке С.
Общий вид операции typedef:
где тип — любой допустимый языком С тип данных; имя — новое имя, назначаемое соответствующему типу данных.
Операция typedef вводит не новые типы, а только синонимы для типов, которые могли бы быть определены другим путем.
Операция typedef напоминает по действию директиву препроцессора #define, но имеет следующие отличия:
В отличие от директивы #define операция typedef дает символические имена, но ограничивается только типами данных.
Операция typedef выполняется компилятором, а не препроцессором.
Область действия определения типа с использованием операции typedef зависит от расположения ключевого слова typedef.
Если определение находится внутри функции, то область действия локальна и ограничена этой функцией. Если определение расположено вне функции, то область действия глобальна по отношению к функциям, содержащимся в соответствующем исходном файле и расположенным после данного определения.
Динамическое распределение памяти
До этого мы работали с переменными (и памятью, выделяемую под нее), которые явно инициализировались в программе. Например, данная операция объявляет в программе массив из 81 символа:
Объявляет в программе массив из 81 элемента. Но что делать, если количество элементов в массиве заранее неизвестно? Или, если обобщить: что делать, если заранее неизвестно, сколько памяти нужно выделить под переменные любого типа?
В языке Си существует механизм динамического распределения памяти, когда память выделяется во время выполнения программы по мере надобности.
Описание функций распределения памяти находятся в файлах stdlib.h.
Размещает блок памяти определенного размера в байтах.
В качестве результата функция возвращает указатель на размещенный блок памяти размером size байт. Если блок такого размера разместить не удается, то функция возвращает значение NULL (константа NULL описана в тех же файлах, в общем-то, равна нулю).
Размещает блок памяти под определенное количество переменных определенного типа (динамический массив).
Возвращает указатель на размещенный блок памяти размером nelem*elsize байт. Если блок такого размера разместить не удается, то функция возвращает значение NULL.
После размещения выделенный блок памяти инициализируется значением 0.
Функция освобождения динамически размещенной памяти
Функции malloc и calloc выделяют память из так называемой «кучи» (heap). Куча — это область памяти, из которой можно брать дополнительную память для размещения данных.
Куча не бесконечна: как и любая память, она может закончиться.
Когда мы динамически выделяем память под нужды программы, мы «занимаем» у кучи на время использования адреса (байты) в памяти. Пока мы используем данную нам кучей память, никая другая программа не может использовать эти адреса.
Если в нашей программе потребность в динамически выделенной памяти отпадает, мы должны ее «вернуть» обратно в кучу, иначе она останется тяжелым грехом на нашей душе занятой. Если гипотетически представить ситуацию, когда вся память из кучи будет занята, но возвращена обратно не будет, то программы не смогут себе выделить память динамически: ведь выделять уже будет нечего.
Кучу можно представить как банк, а вашу программу — как заёмщика. Когда заёмщику нужны деньги, он берет их у банка с обещанием вернуть в установленный срок (в нашем случае, установленный срок будет тогда, когда надобность в динамически выделенной памяти отпадет, например, в конце программы). Если заёмщик заберет все деньги у банка и ничего не вернет, то банк не сможет никому выдать денег, ведь у самого банка их и нет.
В общем случае, память, которую заняла ваша программа у кучи, будет возвращена обратно с помощью механизмов операционной системы. Однако, хороший стиль требует возвращать память во время исполнения программы (кто взял, тот и вернул).
Для возвращения памяти существует функция free:
Пример использования функций динамического распределения памяти.
Давайте рассмотрим использование calloc на примере:
Вводится последовательность целых положительных чисел. Признаком конца последовательности является ввод числа, значение которого не больше 0. Напечатать введенные числа в обратном порядке.

Использование структур и динамически выделенной памяти для реализации структур данных.
Возможность использования динамического распределения памяти позволяет строить достаточно сложные структуры данных, такие как: граф, дерево, стек, очередь и другие.
Для восприятия восхитительной реальности структур данных рекомендуется к прочтению книга Никлауса Вирта Алгоритмы и структуры данных или любой другой источник.
Ниже будет рассмотрен пример построения структуры данных типа стек.
Стек (stack) — абстрактный тип данных, представляющий собой список элементов, организованных по принципу LIFO (англ. last in — first out, «последним пришёл — первым вышел»).
Зачастую стек реализуется в виде однонаправленного списка (каждый элемент в списке содержит помимо хранимой информации в стеке указатель на предыдущий элемент стека).
Структура, описывающая целочисленный элемент стека, может выглядеть так:
Возможны три операции со стеком: добавление элемента (push), удаление элемента (pop) и чтение верхнего элемента стека или всего стека — в зависимости от потребностей программиста.
При проталкивании (push) добавляется новый элемент, указывающий на элемент, бывший до этого головой. Новый элемент теперь становится головным.
При удалении элемента (pop) убирается первый, а головным становится тот, на который был указатель у этого объекта (следующий элемент). При этом pop как функция возвращает значение вытолкнутого элемента.