Menu
Coddy logo textTech

Доступ к элементам родителя

Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 30 из 61.

Теперь, когда мы понимаем встраивание структур и правило первого члена, давайте сосредоточимся на практической стороне: как читать и изменять данные встроенного родителя из функции.

Когда функция получает структуру Child (или указатель на неё), доступ к полям родителя требует навигации через встроенную структуру. Синтаксис зависит от того, работаете ли вы со значением или с указателем.

typedef struct {
    int id;
    int score;
} Parent;

typedef struct {
    Parent parent;
    char grade;
} Child;

Используя указатель на Child, используйте оператор «стрелка» для доступа к родителю, а затем точку для доступа к его членам:

void update_score(Child* c, int new_score) {
    c->parent.score = new_score;  // Стрелка, затем точка
}

void print_info(Child* c) {
    printf("ID: %d, Score: %d\n", c->parent.id, c->parent.score);
}

При использовании значения (передаваемого по копии), используйте точки везде:

void show_id(Child c) {
    printf("ID: %d\n", c.parent.id);  // Точка, затем точка
}

Этот паттерн цепочечного доступа — child->parent.member или child.parent.member — это то, как вы взаимодействуете с унаследованными данными в модели композиции языка C. Это немного более многословно, чем истинное наследование, но это явно и понятно описывает отношения между типами.

challenge icon

Задание

Легко

Давайте создадим систему оценок студентов, которая демонстрирует, как получать доступ и изменять данные внутри вложенной структуры. Вы создадите иерархию, в которой Student содержит вложенную структуру Person, а затем напишете функции, которые работают с данными родительской структуры через дочернюю.

Вы организуете свой код в трех файлах:

  • student.h: Определите иерархию структур с использованием include guards. Создайте структуру Person с полями name (символьный массив из 50 символов) и age (целое число). Затем определите структуру Student, которая включает Person в качестве своего первого члена и добавляет поле grade (целое число, представляющее их балл). Объявите две функции: set_person_age, которая принимает Student* и новое значение возраста, и print_student, которая принимает Student* и отображает всю информацию.
  • student.c: Реализуйте обе функции. Ваша функция set_person_age должна изменять возраст внутри вложенной структуры Person — здесь вы попрактикуетесь в синтаксисе «стрелка-затем-точка» (s->person.age). Ваша функция print_student должна обращаться и отображать все поля как из вложенной структуры person, так и из самой структуры student.
  • main.c: Создайте переменную типа Student и инициализируйте все ее поля. Затем используйте set_person_age, чтобы обновить возраст вложенного человека, и вызовите print_student, чтобы отобразить результат.

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

В вашем основном файле создайте студента с начальными значениями, затем вызовите set_person_age, чтобы изменить возраст на новое значение, и, наконец, вызовите print_student, чтобы отобразить обновленную информацию.

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

Name: Emma
Age: 21
Grade: 85

Где Emma — это имя, 21 — обновленный возраст (не начальный), а 85 — оценка. Ключевым моментом обучения здесь является навигация через вложенную структуру для чтения и записи членов родительской структуры.

Шпаргалка

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

При использовании указателя на дочернюю структуру используйте оператор «стрелка» для обращения к встроенному родителю, а затем точечную нотацию для доступа к его членам:

void update_score(Child* c, int new_score) {
    c->parent.score = new_score;  // Стрелка, затем точка
}

void print_info(Child* c) {
    printf("ID: %d, Score: %d\n", c->parent.id, c->parent.score);
}

При использовании значения (передаваемого по копии) используйте точечную нотацию на всем пути:

void show_id(Child c) {
    printf("ID: %d\n", c.parent.id);  // Точка, затем точка
}

Этот паттерн — child->parent.member или child.parent.member — обеспечивает явный доступ к унаследованным данным в модели композиции C.

Попробуйте сами

#include <stdio.h>
#include <string.h>
#include "student.h"

int main() {
    char name[50];
    int initial_age, grade, new_age;
    
    // Считать входные данные
    fgets(name, 50, stdin);
    name[strcspn(name, "\n")] = '\0';  // Удалить символ новой строки
    scanf("%d", &initial_age);
    scanf("%d", &grade);
    scanf("%d", &new_age);
    
    // TODO: Создать переменную типа Student
    
    // TODO: Инициализировать все поля студента
    // - Скопировать name в имя вложенного person, используя strcpy
    // - Установить возраст вложенного person в значение initial_age
    // - Установить оценку grade студента
    
    // TODO: Вызвать set_person_age, чтобы обновить возраст на new_age
    
    // TODO: Вызвать print_student для вывода результата
    
    return 0;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming