Перейти к основному содержимому

Тип функции

Что такое тип функции

Тип функции определяется её сигнатурой — типом возвращаемого значения и типами параметров. Функции с одинаковой сигнатурой имеют один тип.

int add(int a, int b);      // Тип: int (int, int)
int multiply(int x, int y); // Тот же тип: int (int, int)
float divide(float a, float b); // Другой тип: float (float, float)

Определение типов функций

Сигнатуры функций

#include <stdio.h>

// Функции с разными типами
int intFunction(int a, int b) { // Тип: int (int, int)
return a + b;
}

float floatFunction(float x, float y) { // Тип: float (float, float)
return x * y;
}

void voidFunction(int param) { // Тип: void (int)
printf("Параметр: %d\n", param);
}

char charFunction(void) { // Тип: char (void)
return 'A';
}

int main() {
printf("Демонстрация типов функций:\n");

printf("intFunction(5, 3) = %d\n", intFunction(5, 3));
printf("floatFunction(2.5, 4.0) = %.1f\n", floatFunction(2.5, 4.0));
voidFunction(42);
printf("charFunction() = '%c'\n", charFunction());

return 0;
}

Группировка функций по типам

Функции одного типа

#include <stdio.h>

// Группа функций типа: int (int, int)
int maximum(int a, int b) { return a > b ? a : b; }
int minimum(int a, int b) { return a < b ? a : b; }
int modulo(int a, int b) { return b != 0 ? a % b : 0; }

// Группа функций типа: void (char*)
void printError(char *message) { printf("❌ Ошибка: %s\n", message); }
void printWarning(char *message) { printf("⚠️ Предупреждение: %s\n", message); }
void printInfo(char *message) { printf("ℹ️ Информация: %s\n", message); }

int main() {
// Массив функций одного типа: int (int, int)
int (*mathOperations[3])(int, int) = {maximum, minimum, modulo};
char *mathNames[3] = {"максимум", "минимум", "остаток"};

int x = 17, y = 5;
printf("Числа: %d и %d\n", x, y);

for (int i = 0; i < 3; i++) {
int result = mathOperations[i](x, y);
printf("%s: %d\n", mathNames[i], result);
}

printf("\n");

// Массив функций типа: void (char*)
void (*loggers[3])(char*) = {printError, printWarning, printInfo};
char *messages[3] = {"Файл не найден", "Низкий заряд", "Операция завершена"};

for (int i = 0; i < 3; i++) {
loggers[i](messages[i]);
}

return 0;
}

Typedef для типов функций

Упрощение сложных объявлений

#include <stdio.h>

// Определяем псевдонимы для типов функций
typedef int (*BinaryIntOp)(int, int); // Бинарная операция с int
typedef void (*MessageHandler)(char*); // Обработчик сообщений
typedef float (*Calculator)(float, float); // Калькулятор с float

// Функции соответствующих типов
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }

void logMessage(char *msg) { printf("[LOG] %s\n", msg); }
void debugMessage(char *msg) { printf("[DEBUG] %s\n", msg); }

float addFloat(float a, float b) { return a + b; }
float multiplyFloat(float a, float b) { return a * b; }

int main() {
// Используем typedef для чистых объявлений
BinaryIntOp intOp = add;
MessageHandler logger = logMessage;
Calculator calc = addFloat;

printf("Результат intOp: %d\n", intOp(10, 5));
logger("Система инициализирована");
printf("Результат calc: %.2f\n", calc(3.5, 2.1));

// Легко изменяем поведение
intOp = multiply;
logger = debugMessage;
calc = multiplyFloat;

printf("Новый результат intOp: %d\n", intOp(10, 5));
logger("Режим отладки включен");
printf("Новый результат calc: %.2f\n", calc(3.5, 2.1));

return 0;
}

Функции с одинаковыми типами

Взаимозаменяемость функций

#include <stdio.h>

// Все функции имеют тип: float (float, float, float)
float calculateTriangleArea(float a, float b, float c) {
// Формула Герона (упрощенно)
float s = (a + b + c) / 2;
return s; // Упрощенная версия
}

float calculateVolume(float length, float width, float height) {
return length * width * height;
}

float calculateAverage(float a, float b, float c) {
return (a + b + c) / 3.0;
}

// Функция для выполнения вычислений
void performCalculation(float x, float y, float z,
float (*calculator)(float, float, float),
char *description) {
float result = calculator(x, y, z);
printf("%s (%.1f, %.1f, %.1f) = %.2f\n", description, x, y, z, result);
}

int main() {
float a = 5.0, b = 3.0, c = 4.0;

printf("Демонстрация функций одного типа:\n");

// Все функции имеют одинаковый тип, поэтому взаимозаменяемы
performCalculation(a, b, c, calculateTriangleArea, "Площадь треугольника");
performCalculation(a, b, c, calculateVolume, "Объем параллелепипеда");
performCalculation(a, b, c, calculateAverage, "Среднее арифметическое");

return 0;
}

Полиморфизм через указатели на функции

Различное поведение для одного интерфейса

#include <stdio.h>

// Определяем тип обработчика данных
typedef void (*DataProcessor)(int[], int);

// Различные способы обработки массива
void printAsNumbers(int arr[], int size) {
printf("Числа: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}

void printAsChars(int arr[], int size) {
printf("Символы: ");
for (int i = 0; i < size; i++) {
if (arr[i] >= 32 && arr[i] <= 126) { // Печатные ASCII символы
printf("'%c' ", (char)arr[i]);
} else {
printf("? ");
}
}
printf("\n");
}

void printAsSum(int arr[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += arr[i];
}
printf("Сумма: %d\n", sum);
}

// Универсальная функция обработки
void processData(int data[], int size, DataProcessor processor, char *mode) {
printf("Режим: %s\n", mode);
processor(data, size); // Вызываем переданный обработчик
printf("---\n");
}

int main() {
int values[5] = {65, 66, 67, 68, 69}; // ASCII коды A, B, C, D, E

printf("Один массив — разные способы обработки:\n");

// Тот же массив обрабатывается по-разному
processData(values, 5, printAsNumbers, "Как числа");
processData(values, 5, printAsChars, "Как символы");
processData(values, 5, printAsSum, "Как сумма");

return 0;
}
Ключевые концепции
  • Тип функции определяется сигнатурой (возврат + параметры)
  • Функции одного типа взаимозаменяемы через указатели
  • typedef упрощает работу со сложными типами функций
  • Полиморфизм достигается через указатели на функции одного типа
Практические применения
  • Стратегии алгоритмов — разные способы решения одной задачи
  • Обработчики событий — разные реакции на события
  • Плагины и модули — расширяемая функциональность
  • Конфигурируемое поведение — изменение логики во время выполнения
Важные правила
  • Сигнатуры должны точно совпадать для совместимости
  • Типы параметров и возвращаемого значения должны быть идентичными
  • Имена параметров не влияют на тип функции
  • Модификаторы const влияют на тип функции

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