Функции в C: объявление, определение, вызов, аргументы (по значению и по ссылке), рекурсия
Что такое функция?
Функция — именованный блок кода, выполняющий определённую задачу. Позволяет декомпозировать программу, избегать дублирования, улучшать читаемость и тестируемость.
Каждая C-программа начинается с функции main().
Синтаксис: объявление vs определение
// Объявление (прототип) — обычно в .h файле
тип_возврата имя_функции(список_параметров);
// Определение — реализация (в .c файле)
тип_возврата имя_функции(список_параметров) {
// тело функции
return значение; // если тип не void
}
Компоненты функции
- Тип возврата —
int,double,void, указатель, структура и т.д. - Имя — идентификатор (правила как у переменных).
- Параметры (формальные) — список
тип имячерез запятую.voidесли пусто. - Тело — операторы в
{ }.
Пример: функция максимума
// Объявление
int max(int a, int b);
// Определение
int max(int a, int b) {
return (a > b) ? a : b;
}
// Использование
int main() {
int m = max(10, 20); // m = 20
return 0;
}
Вызов функции
При вызове передаются аргументы (actual parameters) — значения, которые копируются в формальные параметры.
int result = max(x, y); // x, y — аргументы
Порядок вычисления аргументов не определён стандартом (компилятор может вычислять в любом порядке). Не пишите код, зависящий от порядка: func(i++, i++) — UB.
Передача аргументов: по значению vs по ссылке
По значению (Call by Value) — стандарт в C
Создаётся копия аргумента. Изменения параметра внутри функции не влияют на аргумент.
void modify(int x) {
x = 20; // меняет локальную копию
}
int main() {
int a = 10;
modify(a);
printf("%d\n", a); // 10 — не изменилось!
}
По ссылке (Call by Reference) — через указатели
Передаётся адрес переменной. Функция может изменить оригинал.
void modify(int *ptr) {
*ptr = 20; // разыменование: меняем значение по адресу
}
int main() {
int a = 10;
modify(&a); // передаём адрес
printf("%d\n", a); // 20 — изменилось!
}
Идиома: для изменения аргумента передавайте указатель. Для больших структур — передавайте указатель на const (чтение) или не-const (запись).
Возврат значения
return выражение;— завершает функцию, возвращает значение вызывающему.voidфункции:return;или просто достижение}.- Функция с не-void возвратом обязана вернуть значение по всем путям (иначе UB).
Рекурсия
Функция вызывает сама себя. Нужен базовый случай (остановка) и рекурсивный шаг (упрощение задачи).
// Факториал
unsigned long long factorial(unsigned n) {
if (n <= 1) return 1; // базовый случай
return n * factorial(n - 1); // рекурсивный шаг
}
// Фибоначчи (неэффективно без мемоизации)
unsigned fib(unsigned n) {
if (n <= 1) return n;
return fib(n-1) + fib(n-2);
}
Осторожно: глубокая рекурсия → переполнение стека (stack overflow). Для линейных алгоритмов лучше итерация.
Область видимости и время жизни в функциях
- Параметры — как локальные переменные (создаются при входе, уничтожаются при выходе).
- Локальные переменные — авто (стек), время жизни = вызов функции.
- Static локальные — сохраняют значение между вызовами.
Указатели на функции
Функция имеет адрес в памяти. Можно хранить в переменной, передавать как аргумент, возвращать.
// Тип: указатель на функцию, принимающую два int, возвращающую int
int (*operation)(int, int);
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
operation = add;
printf("%d\n", operation(5, 3)); // 8
operation = sub;
printf("%d\n", operation(5, 3)); // 2
// Колбэк (callback) — передача функции как аргумента
void process(int a, int b, int (*op)(int, int)) {
printf("Result: %d\n", op(a, b));
}
process(10, 5, add); // 15
Функции с переменным числом аргументов (variadic)
Используют <stdarg.h>: va_list, va_start, va_arg, va_end.
#include <stdarg.h>
#include <stdio.h>
double average(int count, ...) {
va_list args;
va_start(args, count);
double sum = 0;
for (int i = 0; i < count; i++) {
sum += va_arg(args, double);
}
va_end(args);
return sum / count;
}
// average(3, 1.0, 2.0, 3.0) → 2.0
Inline-функции (C99)
inline int square(int x) { return x * x; }
Подсказка компилятору подставить код вместо вызова. Не гарантируется. Для принуждения: static inline в заголовочнике.
Лучшие практики
- Одна функция — одна задача (Single Responsibility).
- Имена: глаголы
calculate_sum,parse_int,is_valid. - Объявляйте прототипы в
.h, определяйте в.c. - Используйте
constдля параметров-указателей, которые не должны меняться:void print(const int *arr, size_t n). - Проверяйте указатели на
NULLперед разыменованием. - Избегайте глобальных переменных — передавайте контекст через параметры.
- Для массивов передавайте указатель + размер:
void func(int *arr, size_t len).
Язык C
- Четыре проверенных паттерна пользовательских функций в C
- Как передавать одномерные и двумерные массивы в функции в C — практическое руководство
- Стандартные библиотечные функции в C: практическое руководство
- Освоение лямбда-функций Python: практические примеры и лучшие практики
- Мастерство функций в Verilog для эффективного RTL дизайна
- Битовые поля в C: компактное хранение флагов и малых целых чисел
- Освоение ввода-вывода файлов в C: создание, открытие и управление файлами
- Функции с переменным числом аргументов в C: практическое руководство
- Полиморфизм в C++: практическое руководство
- Функции в MATLAB: определение, рабочая область, входные и выходные аргументы