Структуры в с кратко

Обновлено: 05.07.2024

среда, 7 января 2015 г.

Структуры в языке Си.

Видео урок.

Прежде чем говорить о структурах, вспомним массивы. Как вы, наверное, помните, массивы предназначены для хранения однотипных данных. Другими словами каждый элемент массива представляет собой значение определенного типа: целое число, символ, строка. Но зачастую, в программах, требуется хранить в одном месте данные разных типов. В качестве примера, в этом уроке будем рассматривать программу каталог книг. Про каждую книгу нам известно: название, автор, год издания, количество страниц, стоимость.
Типы переменных, используемые для хранения подобных данных очевидны:
char[] – автор, название.
int – год издания, количество страниц.
float – стоимость.

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

int book_date[100]; // дата издания
int book_pages[100]; // количество страниц
char book_author[100][50]; // автор
char book_title[100][100]; // название книги
float book_price[100]; //стоимость

Тогда, обращаясь по i -му номеру к соответствующему массиву, мы могли бы получить требуемую информацию. Например, вот так мы могли бы вывести на экран автора, название и количество страниц четвертой книги (не забываем, что нумерация элементов массива начинается с нуля).

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


Задумаемся, что такое структура в обычном понимании этого слова. Структура – это строение или внутренне устройство какого-либо объекта.
Структура в языке Си – это тип данных, создаваемый программистом, предназначенный для объединения данных различных типов в единое целое.
Прежде чем использовать в своей программе структуру, необходимо её описать, т.е. описать её внутреннее устройство. Иногда это называю шаблоном структуры. Шаблон структуры описывается следующим образом.
На картинке слева, мы описали шаблон структуры с именем point . В любом шаблоне структуры можно выделить две основных части: заголовок (ключевое слово struct и имя структуры) и тело (поля структуры, записанные внутри составного оператора).
Точка с запятой в конце обязательна, не забывайте про неё.

Возвращаясь к нашему примеру, опишем структуру book с полями date, pages, author, title, price соответствующих типов.

struct book
int date; // дата издания
int pages; // количество страниц
char author[50]; // автор
char title[100]; // название книги
float price; // стоимость
>;

Попутно отметим, что в качестве полей структуры могут выступать любые встроенные типы данных и даже другие структуры. Подробнее об этом я расскажу в другом уроке. На имена полей накладываются те же ограничения, что и на обычные переменные. Внутри одной структуры не должно быть полей с одинаковыми именами. Имя поля не должно начинаться с цифры. Регистр учитывается.
После того, как мы описали внутреннее устройство структуры, можно считать, что мы создали новый тип данных, который устроен таким вот образом. Теперь этот тип данных можно использовать в нашей программе.
ПРИМЕЧАНИЕ: Обычно структуры описываются сразу после подключения заголовочных файлов. Иногда, для удобства, описание структур выносят в отдельный заголовочный файл.

Объявление структурной переменной происходит по обычным правилам.
struct book kniga1;

Такое объявление создает в памяти переменную типа book , с соответствующими полями.


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


Отмечу, что я сознательно не касаюсь вопроса о том, как хранится структура в памяти, так как считаю, что для новичков эти тонкости будут излишни.
Кроме того, еще одну удобную интерпретацию структуры дает нам книга K&R. Можно думать о ней, как о строчке в таблице, где столбцами выступают поля структуры.


Если кто-то имел дело с реляционными базами данных (MySQL, Oracle), то вам эта такая интерпретация будет очень знакома.

Хорошо, переменную мы объявили. Самое время научиться сохранять в неё данные, иначе, зачем она нам вообще нужна. Мы можем присвоить значения сразу всем полям структуры при объявлении. Это очень похоже на то, как мы присваивали значения массиву при его объявлении.


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

Для обращения к отдельным полям структуры используется оператор доступа "." . Да-да, обычная точка.
Примеры:

kniga1.pages = 250; // записываем в поле pages переменной kniga1
// значение 250.
printf( "%s-%s %d page" , kniga1.author, kniga1.title, kniga1.pages);

Задание: Проверить, что хранится в полях структуры до того, как им присвоено значение.
Сейчас, в одной переменной типа book храниться вся информация об одной книге. Но так как в каталоге вряд ли будет всего одна книга, нам потребуется много переменных одного и того же типа book . А значит что? Правильно, нужно создать массив таких переменных, то есть массив структур. Делается это аналогично, как если бы мы создавали массив переменных любого стандартного типа.

Каждый элемент этого массива это переменная типа book . Т.е. у каждого элемента есть свои поля date, pages, author, title, price . Тут-то и удобно вспомнить о второй интерпретации структуры. В ней массив структур будет выглядеть как таблица.


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

Обращаясь к kniga[3].author мы обращаемся к четвертой строке нашей таблицы и столбику с именем author . Удобная интерпретация, не правда ли?
На данном этапе мы научились основам работы со структурами. Точнее мы переписали с использованием структур тот же код, который использовал несколько массивов. Кажется, что особой разницы нет. Да, на первый взгляд это действительно так. Но дьявол, как обычно, кроется в мелочах.
Например, очевидно, что в нашей программе нам часто придется выводить данные о книге на экран. Разумным решением будет написать для этого отдельную функцию.
Если бы мы пользуемся несколькими массивами, то эта функция выглядела бы примерно так:

void print_book ( int date, int pages, char *author,
char *title, float price)
printf( "%s-%s %d page.\nDate: %d \nPRICE: %f rub.\n" ,
author, title, pages,date, price);
>

А её вызов выглядел как-то вот так:

Не очень-то и компактно получилось, как вы можете заметить. Легче было бы писать каждый раз отдельный printf(); .
Совсем другое дело, если мы используем структуры. Так как структура представляет собой один целый объект (большую коробку с отсеками), то и передавать в функцию нужно только его. И тогда нашу функцию можно было бы записать следующим образом:

void sprint_book (book temp)
printf( "%s-%s %d page.\nDate: %d \nPRICE: %f rub." ,
temp.author,temp.title, temp.pages,
temp.date, temp.price);
>

И вызов, выглядел бы приятнее:

Вот это я понимаю, быстро и удобно, и не нужно каждый раз писать пять параметров. А представьте теперь, что полей у структуры бы их было не 5, а допустим 10? Вот-вот, и я о том же.
Стоит отметить, что передача структурных переменных в функцию, как и в случае обычных переменных осуществляется по значению. Т.е. внутри функции мы работаем не с самой структурной переменной, а с её копией. Чтобы этого избежать, как и в случае переменных стандартных типов используют указатель на структуру. Там есть небольшая особенность, но об этом я расскажу в другой раз.
Кроме того, мы можем присваивать структурные переменные, если они относятся к одному и тому же шаблону. Зачастую это очень упрощает программирование.
Например, вполне реальная задача для каталога книг, упорядочить книги по количеству страниц.
Если бы мы использовали отдельные массивы, то сортировка выглядела бы примерно так.

for ( int i = 99; i > 0; i--)
for ( int j = 0; j
if (book_pages[j] > book_page[j+1])
//меняем местами значения во всех массивах
int temp_date;
int temp_pages;
char temp_author[50];
char temp_title[100];
float temp_price;

temp_date = book_date[i];
book_date[i] = book_date[j];
book_date[j] = temp_date;

temp_pages = book_pages[i];
book_pages[i] = book_pages[j];
book_pages[j] = temp_pages;

//и так далее для остальных трех массивов
>

Совсем другой дело, если мы используем структуры.

for ( int i = 99; i > 0; i--)
for ( int j = 0; j
if (knigi[j].pages > knigi[j+1].pages)
struct book temp;
temp = knigi[j]; //присваивание структур
knigi[j] = knigi[j+1];
knigi[j+1] = temp;
>

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

Практическое задание:

  • Чтение данных в структуру из файла. В файле запись о каждой книге хранится в следующем формате:

Khnut||Art of programming. T.1||1972||129||764||234.2
Ritchie||The C Programming Language. 2 ed.||1986||80||512||140.5
Cormen||Kniga pro algoritmy||1996||273||346||239

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

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

  • заглавие
  • автор
  • Предмет
  • Книжный идентификатор

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

Чтобы определить структуру, вы должны использовать оператор struct. Оператор struct определяет новый тип данных с более чем одним членом для вашей программы. Формат инструкции struct заключается в следующем:

Структура тега является необязательным , и каждое определение членом является нормальным определение переменной, такие как INT I; или float f; или любое другое допустимое определение переменной. В конце определения структуры перед конечной точкой с запятой вы можете указать одну или несколько структурных переменных, но это необязательно. Вот как вы заявляете структуру книги -

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

Чтобы получить доступ к любому члену структуры, мы используем оператор доступа к члену (.) . Оператор доступа к членству кодируется как период между именем переменной структуры и членом структуры, к которому мы хотим получить доступ. Вы должны использовать ключевое слово struct для определения переменных типа структуры. Ниже приведен пример объяснения использования структуры -

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Структуры как аргументы функции

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

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

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

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

Теперь вы можете сохранить адрес структурной переменной в указанной выше переменной указателя. Чтобы найти адрес структурной переменной, поместите оператор & перед именем структуры следующим образом:

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

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

Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

Ключевое слово typedef

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

Теперь вы можете напрямую использовать книги для определения переменных типа Books без использования ключевого слова struct. Ниже приведен пример -

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

среда, 7 января 2015 г.

Структуры в языке Си.

Видео урок.

Прежде чем говорить о структурах, вспомним массивы. Как вы, наверное, помните, массивы предназначены для хранения однотипных данных. Другими словами каждый элемент массива представляет собой значение определенного типа: целое число, символ, строка. Но зачастую, в программах, требуется хранить в одном месте данные разных типов. В качестве примера, в этом уроке будем рассматривать программу каталог книг. Про каждую книгу нам известно: название, автор, год издания, количество страниц, стоимость.
Типы переменных, используемые для хранения подобных данных очевидны:
char[] – автор, название.
int – год издания, количество страниц.
float – стоимость.

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

int book_date[100]; // дата издания
int book_pages[100]; // количество страниц
char book_author[100][50]; // автор
char book_title[100][100]; // название книги
float book_price[100]; //стоимость

Тогда, обращаясь по i -му номеру к соответствующему массиву, мы могли бы получить требуемую информацию. Например, вот так мы могли бы вывести на экран автора, название и количество страниц четвертой книги (не забываем, что нумерация элементов массива начинается с нуля).

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


Задумаемся, что такое структура в обычном понимании этого слова. Структура – это строение или внутренне устройство какого-либо объекта.
Структура в языке Си – это тип данных, создаваемый программистом, предназначенный для объединения данных различных типов в единое целое.
Прежде чем использовать в своей программе структуру, необходимо её описать, т.е. описать её внутреннее устройство. Иногда это называю шаблоном структуры. Шаблон структуры описывается следующим образом.
На картинке слева, мы описали шаблон структуры с именем point . В любом шаблоне структуры можно выделить две основных части: заголовок (ключевое слово struct и имя структуры) и тело (поля структуры, записанные внутри составного оператора).
Точка с запятой в конце обязательна, не забывайте про неё.

Возвращаясь к нашему примеру, опишем структуру book с полями date, pages, author, title, price соответствующих типов.

struct book
int date; // дата издания
int pages; // количество страниц
char author[50]; // автор
char title[100]; // название книги
float price; // стоимость
>;

Попутно отметим, что в качестве полей структуры могут выступать любые встроенные типы данных и даже другие структуры. Подробнее об этом я расскажу в другом уроке. На имена полей накладываются те же ограничения, что и на обычные переменные. Внутри одной структуры не должно быть полей с одинаковыми именами. Имя поля не должно начинаться с цифры. Регистр учитывается.
После того, как мы описали внутреннее устройство структуры, можно считать, что мы создали новый тип данных, который устроен таким вот образом. Теперь этот тип данных можно использовать в нашей программе.
ПРИМЕЧАНИЕ: Обычно структуры описываются сразу после подключения заголовочных файлов. Иногда, для удобства, описание структур выносят в отдельный заголовочный файл.

Объявление структурной переменной происходит по обычным правилам.
struct book kniga1;

Такое объявление создает в памяти переменную типа book , с соответствующими полями.


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


Отмечу, что я сознательно не касаюсь вопроса о том, как хранится структура в памяти, так как считаю, что для новичков эти тонкости будут излишни.
Кроме того, еще одну удобную интерпретацию структуры дает нам книга K&R. Можно думать о ней, как о строчке в таблице, где столбцами выступают поля структуры.


Если кто-то имел дело с реляционными базами данных (MySQL, Oracle), то вам эта такая интерпретация будет очень знакома.

Хорошо, переменную мы объявили. Самое время научиться сохранять в неё данные, иначе, зачем она нам вообще нужна. Мы можем присвоить значения сразу всем полям структуры при объявлении. Это очень похоже на то, как мы присваивали значения массиву при его объявлении.


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

Для обращения к отдельным полям структуры используется оператор доступа "." . Да-да, обычная точка.
Примеры:

kniga1.pages = 250; // записываем в поле pages переменной kniga1
// значение 250.
printf( "%s-%s %d page" , kniga1.author, kniga1.title, kniga1.pages);

Задание: Проверить, что хранится в полях структуры до того, как им присвоено значение.
Сейчас, в одной переменной типа book храниться вся информация об одной книге. Но так как в каталоге вряд ли будет всего одна книга, нам потребуется много переменных одного и того же типа book . А значит что? Правильно, нужно создать массив таких переменных, то есть массив структур. Делается это аналогично, как если бы мы создавали массив переменных любого стандартного типа.

Каждый элемент этого массива это переменная типа book . Т.е. у каждого элемента есть свои поля date, pages, author, title, price . Тут-то и удобно вспомнить о второй интерпретации структуры. В ней массив структур будет выглядеть как таблица.


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

Обращаясь к kniga[3].author мы обращаемся к четвертой строке нашей таблицы и столбику с именем author . Удобная интерпретация, не правда ли?
На данном этапе мы научились основам работы со структурами. Точнее мы переписали с использованием структур тот же код, который использовал несколько массивов. Кажется, что особой разницы нет. Да, на первый взгляд это действительно так. Но дьявол, как обычно, кроется в мелочах.
Например, очевидно, что в нашей программе нам часто придется выводить данные о книге на экран. Разумным решением будет написать для этого отдельную функцию.
Если бы мы пользуемся несколькими массивами, то эта функция выглядела бы примерно так:

void print_book ( int date, int pages, char *author,
char *title, float price)
printf( "%s-%s %d page.\nDate: %d \nPRICE: %f rub.\n" ,
author, title, pages,date, price);
>

А её вызов выглядел как-то вот так:

Не очень-то и компактно получилось, как вы можете заметить. Легче было бы писать каждый раз отдельный printf(); .
Совсем другое дело, если мы используем структуры. Так как структура представляет собой один целый объект (большую коробку с отсеками), то и передавать в функцию нужно только его. И тогда нашу функцию можно было бы записать следующим образом:

void sprint_book (book temp)
printf( "%s-%s %d page.\nDate: %d \nPRICE: %f rub." ,
temp.author,temp.title, temp.pages,
temp.date, temp.price);
>

И вызов, выглядел бы приятнее:

Вот это я понимаю, быстро и удобно, и не нужно каждый раз писать пять параметров. А представьте теперь, что полей у структуры бы их было не 5, а допустим 10? Вот-вот, и я о том же.
Стоит отметить, что передача структурных переменных в функцию, как и в случае обычных переменных осуществляется по значению. Т.е. внутри функции мы работаем не с самой структурной переменной, а с её копией. Чтобы этого избежать, как и в случае переменных стандартных типов используют указатель на структуру. Там есть небольшая особенность, но об этом я расскажу в другой раз.
Кроме того, мы можем присваивать структурные переменные, если они относятся к одному и тому же шаблону. Зачастую это очень упрощает программирование.
Например, вполне реальная задача для каталога книг, упорядочить книги по количеству страниц.
Если бы мы использовали отдельные массивы, то сортировка выглядела бы примерно так.

for ( int i = 99; i > 0; i--)
for ( int j = 0; j
if (book_pages[j] > book_page[j+1])
//меняем местами значения во всех массивах
int temp_date;
int temp_pages;
char temp_author[50];
char temp_title[100];
float temp_price;

temp_date = book_date[i];
book_date[i] = book_date[j];
book_date[j] = temp_date;

temp_pages = book_pages[i];
book_pages[i] = book_pages[j];
book_pages[j] = temp_pages;

//и так далее для остальных трех массивов
>

Совсем другой дело, если мы используем структуры.

for ( int i = 99; i > 0; i--)
for ( int j = 0; j
if (knigi[j].pages > knigi[j+1].pages)
struct book temp;
temp = knigi[j]; //присваивание структур
knigi[j] = knigi[j+1];
knigi[j+1] = temp;
>

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

Практическое задание:

  • Чтение данных в структуру из файла. В файле запись о каждой книге хранится в следующем формате:

Khnut||Art of programming. T.1||1972||129||764||234.2
Ritchie||The C Programming Language. 2 ed.||1986||80||512||140.5
Cormen||Kniga pro algoritmy||1996||273||346||239

Аннотация: В лекции рассматриваются определения, способы объявления, инициализация структур, методы доступа к данным структуры, размещение структур и определение их размера в памяти, массивы структур.

Цель лекции: изучить понятия, оформления и определения структур, доступа к элементам структур, научиться решать задачи с использованием структур на языке C++.

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

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

Структура – это составной объект , в который входят элементы любых типов, за исключением функций. В отличие от массива, который является однородным объектом (все элементы относятся к одному типу данных), структура может быть неоднородной. Таким образом, структура – это тип данных , сформированный из объектов однородных либо разнообразных типов данных.

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

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

Традиционный пример структуры – строка платежной ведомости . Она содержит такие сведения о служащем, как его полное имя , адрес , номер карточки социального страхования, зарплата и т.д. Некоторые из этих характеристик сами могут быть структурами: например, полное имя состоит из нескольких компонент (фамилии, имени и отчества); аналогично адрес и даже зарплата. Другой пример – из области графики: точка на плоскости есть пара вещественных координат, шар в пространстве моделируется четырьмя вещественными числами и т. д.

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

Использование в программе структуры предполагает вначале определение (объявление) структуры-шаблона (типа структуры) и его структурного объекта (структурной переменной). Возможны их раздельное и совместное определения. Для шаблона структуры (с именем или без него) компилятор не выделяет в памяти компьютера место под структуру, а лишь фиксирует правила, необходимые для формирования структурного объекта (структурной переменной).

В момент определения структурного объекта компилятор выделяет место в памяти, где размещаются все компоненты структуры в соответствии с заданным шаблоном.

Ключевое слово struct сообщает компилятору об объявлении структуры. Допустимы три основные формы объявления структур.

1) С поименованным шаблоном:

где ИмяСтруктурногоТипа – идентификатор типа структуры. Следует обратить внимание на точку с запятой, которой заканчивается определяемая структура;

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

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

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

где СписокСтруктур – список идентификаторов, соответствующих именам структурных объектов.

Структурные объекты можно определить так:

При определении структурного объекта (структурной переменной) СтруктурныйОбъект допустима инициализация его компонентов (полей):

Для вышеприведённого примера возможно следующее:

2) С совмещением определения структуры и структурных объектов:

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

Например, структура типа STUDENT с элементами ФИО и номером зачётной книжки:

При рассматриваемом подходе ИмяСтруктурногоТипа (STUDENT) можно опустить.

В пределах программы допустим один непоименованный тип структуры.

3) С использованием оператора typedef :

где ОбозначениеСтруктурногоТипа – синонимы типа структуры. Это упрощает программу при определении структурных объектов. Кроме того, в упрощённом определении имя типа структуры можно опустить.

Определение структурного объекта для данного случая можно организовать так:

Читайте также: