Как создать массив структур в си
Перейти к содержимому

Как создать массив структур в си

Как создать массив структур в си

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

В данном случае определен массив структур person из 10 элементов.

Используем массив структур в программе:

В массиве people определено 4 объекта person. Хотя при инициализации в массив передается 8 значений <23, "Tom", 32, "Bob", 26, "Alice", 41, "Sam">, но так как каждая структура состоит из двух элементов, то соответственно из этих значений по порядку получается 4 объекта person.

Обращение к элементам массива структур происходит по индексу people[0] . А чтобы обратиться к элементу структуры из массива, после индекса указывается имя элемента структуры: people[i].name

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

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

Здесь в массиве people те же 4 элемента person. Для их просмотра создан указатель *p, который устанавливается на начало массива people. И в цикле получаем элементы структур через этот указатель. После завершения каждой итерации указатель увеличивается на единицу, то есть перемещается в памяти на количество байт, которые занимает одна структура. И эти действия продолжаются пока указатель не дойдет до конца массива, который можно получить через выражение people+n.

Сложные типы данных в Си

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

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

Общая форма объявления структуры:

После закрывающей фигурной скобки > в объявлении структуры обязательно ставится точка с запятой.

Пример объявления структуры

Поля структуры располагаются в памяти в том порядке, в котором они объявлены:
Поля структуры

В указанном примере структура date занимает в памяти 12 байт. Кроме того, указатель *month при инициализации будет началом текстовой строки с названием месяца, размещенной в памяти.

При объявлении структур, их разрешается вкладывать одну в другую.

Инициализация полей структуры

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

  • присвоение значений элементам структуры в процессе объявления переменной, относящейся к типу структуры;
  • присвоение начальных значений элементам структуры с использованием функций ввода-вывода (например, printf() и scanf() ).

В первом способе инициализация осуществляется по следующей форме:

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

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

Результат работы
Инициализация полей структуры

Имя структурной переменной может быть указано при объявлении структуры. В этом случае оно размещается после закрывающей фигурной скобки > . Область видимости такой структурной переменной будет определяться местом описания структуры.

Поля приведенной структурной переменной: number.real, number.imag .

Объединения

Объединениями называют сложный тип данных, позволяющий размещать в одном и том же месте оперативной памяти данные различных типов.

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

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

Общая форма объявления объединения

Объединение

Объединения применяются для следующих целей:

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

Например, удобно использовать объединения, когда необходимо вещественное число типа float представить в виде совокупности байтов

Результат выполнения:
Использование объединений

Пример Поменять местами два младших байта во введенном числе

Результат выполнения
Поменять местами байты в числе

Битовые поля

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

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


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

Пример Разработать программу, осуществляющую упаковку даты в формат
Формат даты

Результат выполнения
Битовые поля

Массивы структур

Работа с массивами структур аналогична работе со статическими массивами других типов данных.

Пример Библиотека из 3 книг

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

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

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

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

указатель — указатель на структуру или объединение;
поле — поле структуры или объединения;

Динамическое выделение памяти для структур

Динамически выделять память под массив структур необходимо в том случае, если заранее неизвестен размер массива. Для определения размера структуры в байтах используется операция sizeof (ИмяСтруктуры) .

Пример Библиотека из 3 книг

Комментариев к записи: 40

struct date
<
int day; // 4 байта
char *month; // 4 байта
int year; // 4 байта
>;

# if ndef department_h
#define department_h

#include <stdio.h>
struct Office
<
char title[50];

struct <
int amount; // количество сотрудников
char lastname[50]; // фамилия начальника
> inc;

struct <
int year;
int month;
> date;

#include <stdio.h> //подключаем основную библиотеку
#include <stdlib.h>
#include <math.h> //подключаем библиотеку математики
#include <locale.h> //подключаем библиотеку языков
#include <string.h>
#include "department.h"
#define N 0

void display( struct Office* a, int i) //вывод одного отдела с помощью цикла
<
printf( "Название: %s\nКоличество сотрудников: %d\nФамилия начальника: %s\nДата создания: %d.%d\n\n" ,
a[i].title, a[i].inc.amount, a[i].inc.lastname, a[i].date.year, a[i].date.month);
>

void create( struct Office* a, int * pn) //функция ввода с клавиутары данных и вывод их на консоль
<
printf( "Введите количество отделов (< 20): " );
scanf_s( "%d%*c" , pn); //%*c применяется для удаления enter и считывания fgets дальше (чтобы они работали и воспринимались программой)
printf( "\n" );
for ( int i = 0; i < *pn; i++)
<
printf( "Название: " );
fgets(a[i].title, 50, stdin);
a[i].title[strlen(a[i].title) — 1] = 0;
printf( "Количество сотрудников: " );
scanf_s( "%d%*c" , &a[i].inc.amount);
printf( "Фамилия начальника: " );
fgets(a[i].inc.lastname, 50, stdin);
a[i].inc.lastname[strlen(a[i].inc.lastname) — 1] = 0;
printf( "Дата создания: " );
scanf_s( "%d%d%*c" , &a[i].date.year, &a[i].date.month);
puts( "" );
>
>

int small( struct Office* a, int n, int year, int month)
<
int i = 0, index = 0, min = 0;
while ((a[i].date.year > year) || (a[i].date.month > month) && (a[i].date.year == year)) //сравниваем дату создания с датой, введенной нами
<
i++;
>

index = i;
min = a[i].inc.amount;

for (i; i < n; i++) //находим самый маленький отдел и сравниваем дату с другими датами создания отдела
<
if ((a[i].inc.amount < min) && (a[i].date.year <= year) || (a[i].inc.amount < min) && (a[i].date.month <= month) && (a[i].date.year == year))
<
min = a[i].inc.amount;
index = i;
>
>
return index;
>

int main()
<
setlocale(LC_ALL, "Rus" ); //включение локализации
setlocale(LC_NUMERIC, "Eng" ); //использование "." в дробных значениях

int n = 0, year = 0, month = 0;

n = sizeof (A) / sizeof (A[0]); //размер (в байтах) всего массива, то есть сумма всех элементов/ размер (в байтах) одной структуры (50)

for ( int i = 0; i < n; i++)
display(A, i);
# else
struct Office A[19];
create(A, &n);

for ( int i = 0; i < n; i++)
display(A, i);

printf( "Введите дату создания: " );
scanf_s( "%d%d%*c" , &year, &month);
printf( "\n" );
n = small(A, n, year, month);
display(A, n);
return 0;
>

Как создать массив структур в си

БлогNot. Структура со строками string и файловые чтение/запись массива таких структур

Структура со строками string и файловые чтение/запись массива таких структур

В отличие от этого примера, используем в структурном типе данных более удобные в обращении и современные строки string и файловые потоки вместо классических си-строк char * и файлов из <cstdio> . Лекции по всем этим темам можно найти в оглавлении.

В итоге должна получиться программка, которая покажет непосредственное задание значений полям структурной переменной и ввод значений полей с консоли, а также запись файла структур и последующее его контрольное чтение. Проверяться она будет в консоли Visual Studio 2015, проект создан как вот здесь.

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

Опишем структурный тип данных, включающий в себя идентификатор (номер) записи, две строки для хранения имени и даты рождения, а также вещественное поле money для хранения, например, зарплаты:

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

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

Функция для ввода данных с консоли input получает аргументами адрес структуры a , куда нужно заносить данные (это может быть, в том числе, и адрес элемента массива структур) и идентификатор записи i , остальные поля записи она запрашивает у пользователя.

В реальности стоило бы снабдить код большим количеством проверок корректности данных и вводом их в некую буферную запись, откуда потом скорректированные данные могут быть скопированы в массив или в файл.

Также обратите внимание, что мы не должны «резать» поля структуры кодом вроде

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

Со всеми этими оговорками, простейшая функция ввода записи получилась такой:

Функция output , соответственно, выводит в консоль поля записи a , переданной аргументом:

При корректных данных должны получиться столбцы правильной ширины.

Главной программе осталось русифицировать консоль и создать массив записей, для простоты предусмотрим там всего 2 элемента:

Первый элемент с id , равным нулю, зададим программно, а второй введём с консоли:

Откроем файл notes.dat для записи и поместим туда данные, заодно печатая их в консоль:

Обратите внимание, что применение sizeof к составному объекту string некорректно, приходится писать в бинарный файл отдельными полями.

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

Вы понимаете, что наш файл — двоичный, и открывать его текстовым редактором бессмысленно.

Вот лог работы нашей программы:

скриншот 16-ричного вида файла (см. комменты)
скриншот 16-ричного вида файла (см. комменты)

  • писать и читать только string.c_str() в бинарные файлы, сведя задачу к предыдущей ссылке (но тогда не нужны и string , а можно делать классически на char * );
  • всё же отказаться от бинарных, а ограничиться текстовыми файлами и парсить полученные через getline строки;
  • предусмотреть в формате файла одновременное сохранение длины строковых данных объекта string .

В последнем случае имеем примерно такой подход (проверен не на структурах, а на паре строк).

Программа для записи набора string в бинарный файл:

Потом читаем это (возможно, с точностью до кодировки):

В моей консоли Visual Studio 2015 обе программки сработали. Итак, если строка string является полем структуры, разницы в подходах нет.

P.P.S. Ну и, раз пошла такая пьянка, теперь разрешим строкам string в массиве структур иметь произвольную длину и содержать пробелы (по умолчанию чтение string из файлового потока прервётся на первом пробельном символе), а сохранять всё будем в текстовом файле, и читать из него же.

Чтобы всё работало, нам пришлось переписать оператор >> для своей структуры.

Программа создаст и затем прочитает такой файл:

При чтении код сам избавится от лишних пробелов в строках между лексемами, следующими после числа.

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

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