Menu
Coddy logo textTech

Константная корректность

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

При написании функций, которые действуют как методы, некоторые из них должны изменять объект (например, player_take_damage), в то время как другие должны только считывать данные, ничего не меняя. Ключевое слово const позволяет обеспечить это различие на уровне компилятора.

Помечая параметр-указатель как const, вы гарантируете, что функция не будет изменять данные, на которые он указывает. Если вы случайно попытаетесь изменить член, компилятор выдаст ошибку:

typedef struct {
    int x;
    int y;
} Point;

// Эта функция может только ЧИТАТЬ точку
void point_print(const Point *self) {
    printf("(%d, %d)\n", self->x, self->y);
    // self->x = 10;  // ОШИБКА: невозможно изменить через константный указатель
}

// Эта функция может ИЗМЕНЯТЬ точку
void point_move(Point *self, int dx, int dy) {
    self->x += dx;
    self->y += dy;
}

Параметр const Point *self сообщает как компилятору, так и другим программистам, что point_print является «геттером» или операцией только для чтения. Это называется константной корректностью — использованием const везде, где модификация не требуется.

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

challenge icon

Задание

Легко

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

Вы создадите три файла:

  • temperature.h: Объявите структуру Temperature с единственным полем double celsius. Также объявите три функции:
    • temp_display — принимает указатель const на Temperature и выводит текущую температуру (только для чтения)
    • temp_adjust — принимает обычный указатель на Temperature и значение типа double, затем добавляет это значение к celsius (изменение)
    • temp_to_fahrenheit — принимает указатель const на Temperature и возвращает значение, преобразованное в градусы Фаренгейта (только для чтения)
    Используйте include guards с символом TEMPERATURE_H.
  • temperature.c: Реализуйте все три функции. Функция temp_display должна выводить данные в формате "Current: %.1fC\n". Функция temp_to_fahrenheit должна возвращать celsius * 9.0 / 5.0 + 32.0. Помните, что функции, использующие указатели const, не могут изменять данные структуры.
  • main.c: Создайте Temperature, выполните операции и выведите результаты.

Вы получите два входных значения: начальное значение в градусах Цельсия (как double) и величину корректировки (как double).

В вашем основном файле (main):

  1. Инициализируйте Temperature начальным значением Цельсия
  2. Вызовите temp_display, чтобы показать начальную температуру
  3. Вызовите temp_adjust, чтобы изменить температуру на величину корректировки
  4. Снова вызовите temp_display, чтобы показать обновленную температуру
  5. Выведите результат преобразования в Фаренгейты, используя temp_to_fahrenheit

Выводите результаты в следующем формате:

Current: {celsius}C
Current: {celsius}C
Fahrenheit: {fahrenheit}F

Используйте %.1f для всех значений температуры, чтобы отображать один десятичный знак.

Например, при начальном значении 20.0 и корректировке 5.0 вывод будет следующим:

Current: 20.0C
Current: 25.0C
Fahrenheit: 77.0F

Шпаргалка

Ключевое слово const может использоваться с параметрами-указателями, чтобы указать, что функция не будет изменять данные, на которые указывает указатель. Это называется const-корректностью.

Когда параметр-указатель помечен как const, компилятор будет предотвращать любые изменения данных через этот указатель:

typedef struct {
    int x;
    int y;
} Point;

// Функция только для чтения — не может изменять точку
void point_print(const Point *self) {
    printf("(%d, %d)\n", self->x, self->y);
    // self->x = 10;  // ОШИБКА: нельзя изменять через const-указатель
}

// Модифицирующая функция — может изменять точку
void point_move(Point *self, int dx, int dy) {
    self->x += dx;
    self->y += dy;
}

Использование const Point *self сообщает как компилятору, так и другим программистам, что функция является операцией только для чтения. Это помогает выявлять ошибки на ранних стадиях: если вы случайно попытаетесь изменить данные в функции, которая не должна ничего менять, компилятор немедленно выдаст ошибку.

Лучшая практика: используйте const для параметров-указателей всякий раз, когда функции не нужно изменять данные. Это делает намерения вашего кода более понятными и предотвращает случайные изменения.

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

#include <stdio.h>
#include "temperature.h"

int main() {
    double initial_celsius, adjustment;
    scanf("%lf", &initial_celsius);
    scanf("%lf", &adjustment);
    
    // TODO: Создайте структуру Temperature и инициализируйте ее значением initial_celsius
    
    // TODO: Вызовите temp_display, чтобы показать начальную температуру
    
    // TODO: Вызовите temp_adjust, чтобы изменить температуру на величину adjustment
    
    // TODO: Снова вызовите temp_display, чтобы показать обновленную температуру
    
    // TODO: Выведите значение в Фаренгейтах, используя temp_to_fahrenheit
    // Формат: "Fahrenheit: %.1fF\n"
    
    return 0;
}
quiz iconПроверьте себя

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

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