6. Глава 6: Функции — Строим Модульный Код

Функции в языке C — это фундаментальный механизм для организации, структурирования и повторного использования кода. Они позволяют разбивать большие и сложные программы на маленькие, управляемые и логически связанные блоки. Думайте о функциях как о «строительных блоках», каждый из которых выполняет свою специфическую задачу.

Преимущества использования функций:

  • Модульность: Программа становится более модульной и легче для понимания.

  • Переиспользование кода: Один раз написанную функцию можно вызвать многократно в разных частях программы, избегая дублирования.

  • Удобство отладки: Локализация ошибок становится проще, так как можно тестировать каждую функцию по отдельности.

  • Сопровождаемость: Изменения в одной функции не влияют на другие, если их интерфейсы остаются неизменными.

6.1. Основные понятия: Объявление и Определение

В языке C каждая функция должна быть объявлена (прототип функции) и определена (реализация функции).

  • Объявление функции (Function Declaration / Prototype): Сообщает компилятору о существовании функции, её имени, типе возвращаемого значения и типах её параметров. Это позволяет компилятору проверить корректность вызовов функции до того, как он увидит её полную реализацию. Обычно объявления размещают в начале файла или в отдельных заголовочных файлах (.h).

  • Определение функции (Function Definition): Содержит фактический код (инструкции), который выполняется, когда функция вызывается. Оно включает тело функции, заключенное в фигурные скобки {}.

Пример объявления и определения функции:

// Пример 6.1: Объявление и Определение Функции Sum
// Объявление (прототип): Сообщает компилятору, что функция 'sum' существует.
// Принимает два целых числа (int) и возвращает целое число (int).
int sum(int a, int b);

// Определение: Реализация функции 'sum'.
// Здесь происходит фактическое сложение двух чисел.
int sum(int a, int b) {
    return a + b; // Возвращаем результат сложения.
}

// Главная функция программы
int main() {
    // Вызов функции 'sum' с аргументами 3 и 4.
    // Результат (7) будет присвоен переменной 'result'.
    int result = sum(3, 4);
    printf("Сумма: %d\n", result); // Выводим полученную сумму.
    return 0; // Завершаем программу успешно.
}

6.2. Аргументы и Возвращаемое Значение

Функции могут принимать аргументы (также называемые параметрами или входными данными) и возвращать результат через оператор return.

  • Аргументы: Это значения, которые передаются в функцию для её обработки. Они указываются в круглых скобках после имени функции в объявлении и определении. Каждый аргумент имеет свой тип и имя.

  • Возвращаемое значение: Это результат, который функция передаёт обратно вызвавшему её коду. Тип возвращаемого значения указывается перед именем функции. Если функция не возвращает никакого значения, используется ключевое слово void.

// Пример 6.2: Функции с void и возвращаемым значением
// Функция без аргументов и без возвращаемого значения (void).
// Просто выводит приветствие.
void print_hello() {
    printf("Привет из функции print_hello!\n");
}

// Функция, принимающая два целых числа и возвращающая их произведение.
int multiply(int x, int y) {
    return x * y; // Возвращаем результат умножения.
}

// Главная функция программы
int main() {
    print_hello(); // Вызываем функцию, которая просто печатает сообщение.

    // Вызываем функцию 'multiply' с аргументами 2 и 3.
    // Результат (6) будет выведен напрямую.
    printf("Произведение: %d\n", multiply(2, 3));
    return 0; // Завершаем программу успешно.
}

Важные аспекты использования функций:

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

  • Гибкость расположения: Вы можете объявить функцию до её использования (например, до main()), а определить её полную реализацию после main() или в другом файле. Это способствует более чистому и организованному коду.

  • Уникальность имен: Каждая функция в вашей программе должна иметь уникальное имя.

  • Область видимости: Переменные, объявленные внутри функции, являются локальными и доступны только внутри этой функции.

6.3. Практический пример: Использование функций

Давайте создадим полноценный файл functions_demo.c, демонстрирующий несколько функций.

// Пример 6.3: Практический пример использования функций
#include <stdio.h> // Включаем стандартную библиотеку ввода-вывода для printf

// Объявления (прототипы) функций.
// Эти строки сообщают компилятору о существовании функций до их определения.
void greet();           // Функция без аргументов и без возвращаемого значения
int square(int n);      // Функция принимает int, возвращает int
double calculate_area(double radius); // Функция принимает double, возвращает double

// Главная функция - точка входа в программу.
int main() {
    // Вызов функции greet().
    greet();

    // Вызов функции square() с аргументом 5 и вывод результата.
    printf("Квадрат 5: %d\n", square(5));

    // Вызов функции calculate_area() с аргументом 3.0 и вывод результата.
    // Обратите внимание на спецификатор формата %lf для double.
    printf("Площадь круга радиусом 3.0: %.2lf\n", calculate_area(3.0));

    return 0; // Успешное завершение программы.
}

// Определение функции greet().
// Эта функция просто печатает приветственное сообщение.
void greet() {
    printf("Добро пожаловать в мир функций C!\n");
}

// Определение функции square().
// Эта функция принимает целое число 'n' и возвращает его квадрат.
int square(int n) {
    return n * n;
}

// Определение функции calculate_area().
// Эта функция принимает радиус 'radius' (тип double)
// и возвращает площадь круга (pi * r^2).
// Для простоты используем 3.14159 в качестве PI.
double calculate_area(double radius) {
    const double PI = 3.14159; // Константа для PI
    return PI * radius * radius;
}

Компиляция:

gcc functions_demo.c -o funcdemo
./funcdemo

Результат:

Добро пожаловать в функции!
Квадрат 5: 25
Площадь круга радиусом 3.0: 28.27

Примечание

Хороший стиль программирования: * Рекомендуется размещать объявления функций в начале файла .c или, что ещё лучше, в заголовочных файлах (файлы с расширением .h). Это обеспечивает единообразие и упрощает управление крупными проектами. * Разделение на файлы: В больших проектах часто каждая функция (или группа связанных функций) определяется в отдельном .c файле, а их объявления помещаются в соответствующий .h файл.

* `man gcc` - Руководство по использованию компилятора GCC.
* `man printf` - Руководство по функции `printf` в стандартной библиотеке C.
* `C Functions (W3Schools) <https://www.w3schools.com/c/c_functions.php>`_ - Дополнительный материал по функциям C (на английском).
* `Function Prototypes in C (GeeksforGeeks) <https://www.geeksforgeeks.org/function-prototypes-in-c/>`_ - Подробнее о прототипах функций (на английском).