Перейти к основному содержимому

Структуры

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

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

Для определения структуры применяется ключевое слово struct. Есть два варианта объявления структуры.

  • Первый вариант. Простое объявление структуры, когда после ключевого слова struct идет только имя структуры: struct имя_структуры;

  • Второй вариант. Объявление структуры с определением. Выглядит это следующим образом:

    struct имя_структуры
    {
    компоненты_структуры
    };

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

 struct student {
uint8_t name[50]; // массив из 50 символов
uint32_t group; // целое число
uint8_t country[30]; // массив из 30 символов
} student1, student2; // переменные с типом struct student

struct student course[200]; // массив из 200 структур
struct student *pst; // указатель на struct student

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

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

Можно объявить структуру с одним и тем же именем несколько раз, но нельзя определить структуру более одного раза.

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

  • По позиции: значения передаются элементам структуры в том порядке, в котором они следуют в структуре:

    struct person
    {
    int age;
    char * name;
    };

    struct person tom = {23, "Tom"};
  • По имени: значения передаются элементам структуры по имени, независимо от порядка:

    struct person tom = {.name="Tom", .age=23};

После создания переменной структуры можно обращаться к ее элементам - получать их значения или, наоборот, присваивать им новые значения.Для обращения к полю структуры используется оператор “.” (точка).

#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;
}

Можно инициализировать элементы структуры по отдельности:

struct person tom;
tom.name ="Tom";
tom.age = 22;

Можно одновременно совмещать определение типа структуры и ее переменных:

#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;
}

При обращении через указатель можно использовать оператор разыменования — “*” или оператор стрелка — “→”

подсказка

У операторов точка и стрелка самый высокий приоритет !

Можно определить сразу несколько переменных:

struct person
{
int age;
char * name;
} tom, bob, alice;

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

struct
{
int age;
char * name;
} tom;

typedef

Еще один способ определения структуры представляет ключевое слово 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

Еще один способ определить структуру представляет применение препроцессорной директивы #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];}

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

На структуры, как и на объекты других типов, можно определять указатели. Например, указатель на структуру person: struct person p*

Указатели на структуры можно создавать и для безымянных структурных типов:

struct
{
int age;
char name[20];
} *p1, *p2;

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

struct person kate = {31, "Kate"};
struct person *p_kate = &kate;

Используя указатель на структуру, можно получить доступ к ее элементам. Для этого можно воспользоваться двумя способами. Первый способ представляет применение операции разыменования: (*указатель_на_структуру).имя_элемента

Второй способ предполагает использование операции -> (операция стрелка): указатель_на_структуру->имя_элемента

Пример:

#include <stdio.h>

struct person
{
int age;
char name[20];
};

int main(void)
{
struct person kate = {31, "Kate"};
// указатель на переменную kate
struct person * p_kate = &kate;

// получаем значение элемента name
// получаем значение элемента age
char * name = p_kate->name;
int age = (*p_kate).age;

printf("name = %s \t age = %d \n", name, age);

// изменим элемент age в структуре
p_kate->age = 32;
printf("name = %s \t age = %d \n", kate.name, kate.age);
return 0;
}

Здесь определяется указатель p_kate на переменную kate. И используя указатель, мы можем получить или изменить значения элементов структуры.

Ссылка структуры на саму себя

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

struct node
{
char* value; // значение
struct node* next; // указатель на следующий узел
};

Структура node имеет два элемента. Элемент value хранит собственно некоторое значение. А элемент next представляет указатель на следующий объект структуры node. Как мы можем использовать эту структуру и зачем нам указатель на следующий объект node? Рассмотрим следующий пример:

```c showLineNumbers
#include <stdio.h>

struct node
{
char* value;
struct node* next;
};

int main(void)
{
struct node kate = {.value = "Kate"};
struct node tom = {.value = "Tom" };
struct node bob = {.value = "Bob"};

kate.next = &tom; // Kate - Tom
tom.next = &bob; // Tom - Bob

// устанавливаем указатель на первую структуру в цепочке
struct node * pointer = &kate;
while(pointer != NULL)
{
printf("value = %s \n", pointer->value);
pointer = pointer->next; // переходим к следующему объекту
}
return 0;
}

Самостоятельная работа

Задачи

Задача 1

Что будет напечататно?

student1.group = 101;
strcpy(student1.name, "Ivan");
strcpy(student1.country, "Russia");
student2 = student1;
printf("%s\n", student2.name);
Ответ

Ivan

Задача 2

Описать структуру для представления информации о человеке: фамилия (не более 30 символов), имя (не более 30 символов), возраст. Описать функцию, которая для заданного массива из описанных структур определяет: a. Возраст самого старшего человека b. Количество людей с заданным именем (имя также является параметром функции) c. Количество людей, у которых есть однофамильцы

Решение
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define STR_SIZE 30
#define STUDEN_NUMBER 200

struct student {
char surname[STR_SIZE];
char name[STR_SIZE];
uint8_t age;
};

//возраст самого старшего человека;
int Eldest(struct student* course,int number){
int max = course->age;

for(int i=1;i<number;i++)
if(max < (course+i)->age)
max = (course+i)->age;

return max;
}

//количество людей с заданным именем (имя также является параметром функции);
int SameNameNumber(struct student* course,int number,char* name){
int counter = 0;

for(int i=0;i<number;i++)
if(!strcmp(course[i].name,name))
counter++;

return counter;
}

//количество людей, у которых есть однофамильцы;
int Namesakes(struct student* course,int number){
int counter=0;

for(int i=0;i<number-1;i++)
for(int j=i+1;j<number;j++)
if(!strcmp(course[i].surname, course[j].surname)){
counter++;
break;
}

return counter;
}

void AddStudent(struct student* course, int number,char* surname,char* name,int age){
course[number].age = age;
strcpy(course[number].name,name);
strcpy(course[number].surname,surname);
}

void print(struct student* course,int number){
for(int i=0;i<number;i++)
printf("%s\t%s\t%d\n", course[i].surname, course[i].name, course[i].age);
}

int AddCourse(struct student* course){
int c=0;

AddStudent(course,c++,"Ivanov","Ivan",18);
AddStudent(course,c++,"Petrov","Ivan",19);
AddStudent(course,c++,"Petrov","Ivan",19);
AddStudent(course,c++,"Petrov","Ivan",19);
AddStudent(course,c++,"Petrov","Ivan",19);
AddStudent(course,c++,"Ivanov","Vasily",44);

return c;
}

int main(void){
struct student course1[STUDEN_NUMBER]; // массив из 200 структур
struct student course2[STUDEN_NUMBER]; // массив из 200 структур
int number1=AddCourse(course1);
int number2=AddCourse(course2);

print(course1,number1);
printf("Eldest student = %d\n",Eldest(course1,number1)); char* name = {"Ivan"};
printf("Name %s number = %d\n",name,SameNameNumber(course1,number1,name));
printf("Same surname number = %d\n",Namesakes(course1,number1));

return 0;
}