В данном уроке мы познакомимся со структурами, для чего они нужны и как мы их можем использовать в наших программах.
Нередко в программе может потребоваться объединить несколько переменных различного типа в какой-то один набор для достижения той или иной общей цели. Например, нам нужно описать какой-нибудь товар и создать для него какую-то переменную, которая могла бы хранить ряд его свойств — наименование полное, наименование краткое, вес, цену, количество, единицу измерения и так далее. Эти свойства здесь имеют, как мы видим, различный тип данных, здесь есть и строки, есть и числа с плавающей точкой, могут быть и другие типы данных, а объединить мы их все хотим в какую-то общую группу, присвоить ей какое-то своё отдельное имя, создавать потом с этим именем переменные и обращаться к ним по данному имени, чтобы получить затем вышеперечисленные значения, а также их изменять в ходе нашей программы.
Вот именно в такой ситуации нам на помощь и приходят структуры.
Структура в языке C — это сгруппированные (ая) под одним именем одна или несколько переменных (возможно, различных типов). В качестве этих переменных могут быть также массивы данных, указатели на различные типы данных, также переменные других типов структур и т.д.
Вот общий вид объявления структуры
Это самое классическое объявление структур, но используется часто.
Вот так например, мы можем описать структуру для какого-то товара
struct goods
{
char nm[60];
float amount;
char measure[20];
float price;
float wieght;
};
Затем в коде мы можем объявлять переменные типа нашей структуры вот таким образом
struct goods st1;
Затем мы можем обращаться к данным нашей структуры через точку.
Давайте проинициализируем переменные нашей структуры, которые, кстати, называются ещё полями
strcpy(st1.nm, "Цемент");
st1.amount = 551.;
strcpy(st1.measure, "мешок");
st1.price = 350.35;
st1.wieght = 50.;
Объявлять наши структуры мы можем также и через стандартное объявление типов с помощью директивы typedef, тогда уже имя типа данных нашей структуры будет следовать после блока с переменными
typedef struct
{
char nm[60];
float amount;
char measure[20];
float price;
float wieght;
} goods;
Переменные типов таких структур мы уже объявляем без ключевого слова struct
goods st1;
После этого мы с данной переменной работаем таким же образом, как и с переменными типов структур, объявленных классическим способом.
Также не лишним будет упомянуть, что переменные наших структур могут использоваться в функциях в качестве возврата. Это один из способов вернуть сразу несколько значений разного типа из функции.
Инициализировать поля переменных типов структур мы можем также и во время их объявления.
Например, наш товар мы можем описать вот таким способом
goods st1 = {"Цемент", 551., "мешок", 350.35, 50.};
Также структуры можно объявлять с помощью создания указателей на них, но это будет рассмотрено в более поздних уроках, когда мы познакомимся с указателями и их использованием.
А, думаю, что пока нам теории по структурам вполне достаточно.
Давайте теперь поработаем со структурами практически.
Создадим проект с именем MYPROG25, который был сделан на основе проекта прошлого урока с именем MYPROG24.
Откроем наш проект в Eclipse, произведём его первоначальную настройку и удалим весь наш код из функции main() за исключением возврата, а то больно много там всего набралось. Функция main() приобретёт вот такой вид
int main()
{
return 0; //Return an integer from a function
}
Удалим также объявление перечисления, в том числе и закомментированное, также объявление переменной пользовательского типа. Удалим функции print_res и my_counter с их телами, а также прототипы на них.
Представим, что у нас есть задача описать несколько студентов, у которых будут ФИО, возраст и информация о том, на каком курсе данный студент обучается, так сказать, минимальная информация.
Объявим вот такую глобальную структуру (хотя если мы с данной структурой хотим работать только в рамках какого-то блока, то она вполне имеет право быть и локальной)
1 2 3 4 5 6 7 8 9 |
#include "utils.h" //---------------------------------------------- struct student { char nm[60]; unsigned char age; unsigned char course; }; //---------------------------------------------- |
Мы объявили структуру, с именем student, в которой присутствуют переменные (поля) — строковый массив и две беззнаковых целочисленных переменных, также имеющих свои имена.
В функции main() объявим переменную типа нашей структуры
1 2 3 |
int main() { struct student st1; |
Проинициализируем её поля
1 2 3 4 |
struct student st1; strcpy(st1.nm, "Иванов Иван Иванович"); st1.age = 18; st1.course = 2; |
Мы объявили переменную нашей структуры, и заполнили её поля, тем самым мы получили переменную, в которой описали одного студента — его ФИО, возраст и курс. Выведем эти данные в консоль
1 2 |
st1.course = 2; printf("%s,\t%d курс,\t%d лет", st1.nm, st1.course, st1.age); |
Соберём код и посмотрим, что у нас получилось
Если у кого-то некорректно выводится кириллица, то настройки находятся в Window -> Preferences вот в этой ветке
Добавим ещё одного студента и выведем в консоль и его данные аналогичным образом
1 2 3 4 5 6 7 |
printf("%s,\t%d курс,\t%d лет", st1.nm, st1.course, st1.age); printf("\n"); struct student st2; strcpy(st2.nm, "Перов Петр Иванович"); st2.age = 17; st2.course = 1; printf("%s,\t%d курс,\t%d лет", st2.nm, st2.course, st2.age); |
Посмотрим результат в консоли
Отлично!
Попробуем теперь объявить тип данных структуры с помощью директивы typedef. Закомментируем предыдущее объявление структуры и объявим её следующим образом
1 2 3 4 5 6 7 8 9 10 |
unsigned char course; }; */ typedef struct { char nm[60]; unsigned char age; unsigned char course; } student; //---------------------------------------------- |
В main в объявлениях переменных типа нашей структуры теперь мы можем ключевое слово struct убрать
struct student st1;
struct student st1;
После этого наш код будет также успешно работать.
Только вот если у нас будет много студентов, то так работать с ними будет неудобно. Поэтому закомментируем наш код и объявим массив таких структур
1 2 3 |
printf("%s,\t%d курс,\t%d лет", st2.nm, st2.course, st2.age); */ student st[20]; |
Инициализируем несколько элементов нашего массива и выведем наших студентов в цикле в консоль
1 2 3 4 5 6 7 8 9 10 11 12 |
student st[20]; strcpy(st[0].nm, "Иванов Иван Иванович"); st[0].age = 18; st[0].course = 2; strcpy(st[1].nm, "Петров Петр Иванович"); st[1].age = 17; st[1].course = 1; strcpy(st[2].nm, "Сидоров Александр Петрович"); st[2].age = 22; st[2].course = 4; for(int i=0; i<3; i++) printf("%-30s%d курс\t%d лет\n", st[i].nm, st[i].course, st[i].age); |
Посмотрим результат
Объявление массива оставим, остальной код закомментируем.
Создадим глобальный счётчик наших студентов
1 2 3 |
} student; //---------------------------------------------- unsigned char student_counter = 0; |
Ниже функции main() добавим вот такую функцию
1 2 3 4 5 6 7 8 9 10 11 |
//-------------------------------------------------------- student addStudent (char ch[], unsigned int cr, unsigned int ag) { student res_st; strcpy(res_st.nm, ch); res_st.course = cr; res_st.age = ag; student_counter++; return res_st; } //-------------------------------------------------------- |
Данная функция будет получать в качестве параметров ФИО, курс и возраст нашего студента, объединять всё это в переменную структуры и возвращать её.
Добавим на нашу функцию прототип
1 2 3 4 |
float yf, zf; //---------------------------------------------- student addStudent (char ch[], unsigned int cr, unsigned int ag); //---------------------------------------------- |
Теперь в функции main() добавим вот такой код
1 2 3 4 5 6 7 8 9 10 11 12 |
printf("%-30s%d курс\t%d лет\n", st[i].nm, st[i].course, st[i].age); */ if(student_counter<20) st[student_counter] = addStudent("Иванов Иван Иванович", 2, 18); if(student_counter<20) st[student_counter] = addStudent("Петров Петр Иванович", 1, 17); if(student_counter<20) st[student_counter] = addStudent("Сидоров Александр Петрович", 4, 22); if(student_counter<20) st[student_counter] = addStudent("Попов Иван Сергеевич", 3, 22); if(student_counter<20) st[student_counter] = addStudent("Васильев Федор Николаевич", 5, 24); if(student_counter<20) st[student_counter] = addStudent("Саблин Виктор Петрович", 1, 18); if(student_counter<20) st[student_counter] = addStudent("Веселкин Алексей Алексеевич", 2, 18); if(student_counter<20) st[student_counter] = addStudent("Трухин Сергей Сергеевич", 5, 23); for(int i=0; i<student_counter; i++) printf("%-30s%d курс\t%d лет\n", st[i].nm, st[i].course, st[i].age); |
Данный код будет добавлять студента с его данными в массив, проверяя, не превысили ли мы максимальное количество элементов нашего массива, так как язык такие вещи самостоятельно не контролирует. Затем мы в цикле выводим в консоль наши данные. Проверим работу нашего кода
Всё выводится.
Итак, на данном занятии мы познакомились со структурами, для чего они нужны и как мы можем их использовать в нашем коде.
Всем спасибо за внимание!
Предыдущий урок Программирование на C Следующий урок
Смотреть ВИДЕОУРОК (нажмите на картинку)
student st[0]; выдаёт ошибку «could not be resolved». Пересборка индексов командой «Project->C/C++ Index->Re-resolve unresolved includes» не даёт результата. Команда «Project->C/C++ Index->Search for unresolved includes» возвращает «0 unresolved includes». Понимаю, что это ошибка самого Eclipse. Лезть в Error Parsers боюсь, т.к. ничего о нём не знаю.
Решено. Пришлось переписать Makefile. Извиняюсь за беспокойство.
Описали, объявили-понятно
Вдруг — хрясь! — strcpy
Что это, зачем это? Посмотреть на каком-то другом сайте? Так может и про структуры там же прочитать?
Почему Вы не возмутились на printf? Про strcpy рассказано в уроке 9(правда кратенько…)
Откуда взялось st1 после имени структуры в объявлении? Ни на одном сайте не пишут, откуда это берется и что это такое. Такое ощущение, что сами не знают…
st1 — это имя переменной. goods — имя структуры. Мы объявили переменную st1 типа структуры goods.
Снимаю перед вами шляпу, ибо еще в далеком 88 году (и еще несколько лет потом) писал на С программы, но даже с таким «багажом» узнаю не мало интересного по ходу пьесы )) Но иногда впадаю просто в ступор. Попробую обьяснить почему на примере концовки урока. Проверяем мы выход за границы массива структур в теле main, а вот индекс массива меняем … в процедуре. Я сначала подумал что запись создастся одна (ибо ожидал увидеть ++ индекса в майн))) При таком подходе как у Вас логичнее было бы и массив структур делать глобальным (и часто так и делается в реальных проектах) и, если уж индекс мы меняем в процедуре, в ней же и проверять выход за границы масива и вносить/не вносить данные соответственно. Такие «места» в логике потом очень большое недоумение вызывают, когда программа вроде работает и без ошибок, но не так как хотелось бы )) Все таки новичков учите. Но это я так. Брюзжу наверное… Большое спасибо за уроки, особенно по микропроцессорам. Там реально не паханный для меня край ))
Подвохи классные.