Структура в языке программирования Си представляет собой составной тип данных, который состоит из других компонентов. При этом в отличие от массива эти компоненты могут представлять различные типы данных.
Для определения структуры применяется ключевое слово struct, а сам формат определения выглядит следующим образом:
struct имя_структуры { компоненты_структуры };
Имя_структуры представляет произвольный идентификатор, к которому применяются те же правила, что и при наименовании переменных.
После имени структуры в фигурных скобках помещаются компоненты структуры - объекты, которые составляют структуру.
Следует отметить, что в отличие от функции при определении структуры после закрывающей фигурной скобки идет точка с запятой.
Например, определим простейшую структуру:
struct person { int age; char * name; };
Здесь определена структура person
, которая имеет два элемента: age
(представляет тип int
) и name
(представляет указатель на тип char
).
Все элементы структуры объявляются как обычные переменные. Но в отличие от переменных при определении элементов структуры для них не выделяется память, и их нельзя инициализировать. По сути мы просто определяем новый тип данных.
После определения структуры мы можем ее использовать. Для начала мы можем определить объект структуры - по сути обычную переменную, которая будет представлять выше созданный тип:
// определение структуры person struct person { int age; char * name; }; int main(void) { // определение переменной, которая представляет структуру person struct person tom; }
Здесь определена переменная tom, которая представляет структуру person
. И при каждом определении переменной типа структуры ей будет выделяться
память, необходимая для хранения ее элементов.
При определении переменной структуры ее можно сразу инициализировать, присвоив какое-нибудь значение. Инициализация структур аналогична инициализации массивов: в фигурных скобках передаются значения для элементов структуры. Есть два способа инициализации структуры.
По позиции: значения передаются элементам структуры в том порядке, в котором они следуют в структуре:
struct person tom = {23, "Tom"};
Так как в структуре person первым определено свойство age, которое представляет тип int - число, то в фигурных скобках вначале идет число, которое передается элементу age
.
Вторым идет элемент name
, который представляет указатель на тип char
или строку, соответственно вторым идет строка.
И так далее для всех элементов структуры по порядку.
По имени: значения передаются элементам структуры по имени, независимо от порядка:
struct person tom = {.name="Tom", .age=23};
В этом случае перед именем элемента указывается точка, например, .name
.
Также после создания переменной структуры можно обращаться к ее элементам - получать их значения или, наоборот, присваивать им новые значения. Для обращения к элементам структуры используется операция "точка":
имя_переменной_структуры.имя_элемента
Теперь объединим все вместе в рамках программы:
#include <stdio.h> struct person { int age; char * name; }; int main(void) { struct person tom = {23, "Tom"}; printf("Age: %d \t Name: %s", tom.age, tom.name); return 0; }
Консольный вывод программы:
Age: 23 Name: Tom
Можно инициализировать элементы структуры по отдельности:
#include <stdio.h> struct person { int age; char * name; }; int main(void) { struct person tom; tom.name ="Tom"; tom.age = 22; printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
Мы можем одновременно совмещать определение типа структуры и ее переменных:
#include <stdio.h> struct person { int age; char * name; } tom; // определение структуры и ее переменной int main(void) { tom = {38, "Tom"}; printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
После определения структуры, но до точки с запятой мы можем указать переменные этой структуры. А затем присвоить их элементам значения.
Можно тут же инициализировать структуру:
#include <stdio.h> struct person { int age; char * name; } tom = {38, "Tom"}; int main(void) { printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
Можно определить сразу несколько переменных:
struct person { int age; char * name; } tom, bob, alice;
При подобном определении мы можем даже не указывать имя структуры:
struct { int age; char * name; } tom;
В этом случае компилятор все равно будет знать, что переменная tom представляет структуры с двумя элементами name и age. И соответственно мы также с этими переменными сможем работать. Другое дело, что мы не сможем задать новые переменные этой структуры в других местах программы.
Еще один способ определения структуры представляет ключевое слово typedef:
#include <stdio.h> typedef struct { int age; char * name; } person; int main(void) { person tom = {23, "Tom"}; printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
В конце определения структуры после закрывающей фигурной скобки идет ее обозначение - в данном случае person
.
В дальнейшем мы можем использовать это обозначение для создания переменной структуры. При этом в отличие от примеров выше здесь при определении переменной не надо
использовать слово struct.
Еще один способ определить структуру представляет применение препроцессорной директивы #define:
#include <stdio.h> #define PERSON struct {int age; char name[20];} int main(void) { PERSON tom = {23, "Tom"}; printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
В данном случае директива define определяет константу PERSON, вместо которой при обработке исходного кода препроцессором будет вставляться
код структуры struct {int age; char name[20];}
Одну структуру можно присвавивать другой структуре того же типа. При копировании элементы структуры получают копии значений:
#include <stdio.h> struct person { int age; char * name; }; int main(void) { struct person tom = {38, "Tom"}; // копируем значения из структуры tom в структуру bob struct person bob = tom; bob.name = "Bob"; printf("Name: %s \t Age: %d \n", bob.name, bob.age); printf("Name: %s \t Age: %d \n", tom.name, tom.age); return 0; }
Здесь в переменную bob
копируются данные из структуры tom
. Далее мы для структуры bob
меняется значение поля name
.
В итоге мы получим следующий консольный вывод:
Name: Bob Age: 38 Name: Tom Age: 38
С элементами структуры можно производить все те же операции, что и с переменными тех же типов. Например, добавим ввод с консоли:
#include <stdio.h> struct person { int age; char name[20]; }; int main(void) { struct person tom = {23, "Tom"}; printf("Enter name: "); scanf("%s", tom.name); printf("Enter age: "); scanf("%d", &tom.age); printf("Name:%s \t Age: %d", tom.name, tom.age); return 0; }
Консольный вывод программы:
Enter name: Eugene Enter age: 33 Name: Eugene Age: 33