Массивы структур и функции

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

Если структура описывает одну запись, то массив структур — набор записей:

typedef struct
{
    int id;
    int course;
    double score;
} Student;

Student group[100];
int n;

Ввод:

if (scanf("%d", &n) != 1 || n < 0 || n > 100)
{
    return 1;
}

for (int i = 0; i < n; i++)
{
    if (scanf("%d %d %lf",
              &group[i].id,
              &group[i].course,
              &group[i].score) != 3)
    {
        return 1;
    }
}

Вывод:

for (int i = 0; i < n; i++)
{
    printf("%d %d %.2f\n",
           group[i].id,
           group[i].course,
           group[i].score);
}

Динамический массив структур

В лабораторной работе количество записей заранее неизвестно. Выделяем память после ввода N:

Student *group = malloc((size_t)n * sizeof(*group));
if (group == NULL && n != 0)
{
    return 1;
}

sizeof(*group) — размер одной структуры. Не используйте sizeof(Student *): это размер указателя, а не записи. Освобождение — одним вызовом: free(group);.

Передача массива структур в функцию

Массив при передаче в функцию становится указателем на первый элемент. Поэтому параметр имеет вид Student *group, int n:

void print_group(const Student *group, int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d %d %.2f\n",
               group[i].id,
               group[i].course,
               group[i].score);
    }
}

Вызов: print_group(group, n); — только имя массива, без &.

const означает, что функция не должна менять записи. Если нужно изменить — уберите const:

void add_bonus(Student *group, int n, double bonus)
{
    for (int i = 0; i < n; i++)
    {
        group[i].score += bonus;
    }
}

Внутри функции group[i] — это структура; чтобы изменить поле, не нужен дополнительный &, потому что group[i] уже обозначает саму запись.

Сортировка массива структур

Сортировать можно по любому полю. Меняем местами целые структуры:

for (int i = 0; i < n - 1; i++)
{
    for (int j = 0; j < n - 1 - i; j++)
    {
        if (group[j].score < group[j + 1].score)
        {
            Student tmp = group[j];
            group[j] = group[j + 1];
            group[j + 1] = tmp;
        }
    }
}

Это сортировка по убыванию score. Для составного ключа сравните сначала course, затем score:

if (group[j].course > group[j + 1].course ||
    (group[j].course == group[j + 1].course &&
     group[j].score < group[j + 1].score))
{
    /* обменять */
}

Для устойчивой сортировки при равных ключах сохраняйте исходный порядок.

Поиск по полю

Поиск по id:

int find_by_id(const Student *group, int n, int id)
{
    for (int i = 0; i < n; i++)
    {
        if (group[i].id == id)
        {
            return i;
        }
    }
    return -1;
}

Поиск по course:

int find_by_course(const Student *group, int n, int course)
{
    for (int i = 0; i < n; i++)
    {
        if (group[i].course == course)
        {
            return i;
        }
    }
    return -1;
}

Функция возвращает индекс или -1. Вызывающий код обязан проверить результат.

Вставка и удаление записей

Для вставки в середину нужно сдвинуть элементы вправо, начиная с последнего:

void insert_at(Student *group, int *n, int pos, Student value)
{
    for (int i = *n; i > pos; i--)
    {
        group[i] = group[i - 1];
    }
    group[pos] = value;
    (*n)++;
}

Для удаления — сдвинуть элементы влево:

void remove_at(Student *group, int *n, int pos)
{
    for (int i = pos; i < *n - 1; i++)
    {
        group[i] = group[i + 1];
    }
    (*n)--;
}

При вставке проверяйте, что массив имеет запас по размеру. В лабораторной работе для динамического массива используйте realloc или выделяйте память с запасом.

Пример программы

#include <stdio.h>
#include <stdlib.h>

typedef struct
{
    int id;
    int course;
    double score;
} Student;

int input_group(Student *group, int n);
void print_group(const Student *group, int n);
int find_best(const Student *group, int n);

int main(void)
{
    int n;
    if (scanf("%d", &n) != 1 || n < 0)
    {
        return 1;
    }

    Student *group = malloc((size_t)n * sizeof(*group));
    if (group == NULL && n != 0)
    {
        return 1;
    }

    if (!input_group(group, n))
    {
        free(group);
        return 1;
    }

    print_group(group, n);

    if (n > 0)
    {
        int best = find_best(group, n);
        printf("Лучший: %d %.2f\n", group[best].id, group[best].score);
    }

    free(group);
    return 0;
}

int input_group(Student *group, int n)
{
    for (int i = 0; i < n; i++)
    {
        if (scanf("%d %d %lf",
                  &group[i].id,
                  &group[i].course,
                  &group[i].score) != 3)
        {
            return 0;
        }
    }
    return 1;
}

void print_group(const Student *group, int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d %d %.2f\n",
               group[i].id,
               group[i].course,
               group[i].score);
    }
}

int find_best(const Student *group, int n)
{
    int best = 0;
    for (int i = 1; i < n; i++)
    {
        if (group[i].score > group[best].score)
        {
            best = i;
        }
    }
    return best;
}

Частые ошибки

ОшибкаЧто не так
sizeof(Student *) при mallocвыделен размер указателя, а не записи
group[i].score без & в scanfзапись по «мусорному» адресу
n не изменили после вставки/удаленияпоследний элемент остаётся «лишним»
сдвиг при вставке начали с pos, а не с концазатирание элементов
не проверили mallocпадение при нехватке памяти
забыли freeутечка памяти
сравнили структуры через ==в C структуры нельзя сравнивать целиком

Документация

Что запомнить

  1. Массив структур — набор записей; поля доступны через group[i].field.
  2. Динамический массив структур выделяют через malloc(n * sizeof(*group)).
  3. В функцию передают указатель на первый элемент и количество записей.
  4. const защищает данные от случайного изменения.
  5. Сортировка меняет местами целые структуры; сравнение составного ключа — вручную.
  6. При вставке и удалении не забывайте менять n и проверять границы.

Подтверждение

—