====== функции ====== Процедура - такая ф-я, которая имееет побочный эффект (те затрагивает части программы за пределами себя) и, как правило, ничего не возвращает. // определение функции возвращаемый_тип имя_функции(параметры) { выполняемые_инструкции; } // вызов функции имя_функции(параметры); ===== Прототип или описание функции ===== Функция должна быть определена до ее вызова. Большенство компиляторов не скомпилирует данный код: #include int main(void){ hello(); // вызов до определения - ошибка return 0; } void hello(){ printf("Hello!\n"); } Для определения функции после ее вызова сущевствуют **прототипы** функций: #include // прототип void hello(void); int main(void){ hello(); return 0; } // определение void hello(){ printf("Hello!\n"); } Таким образом компилятор, на момент вызова ф-ии, уже будет знать о ней. ===== Параметры ===== // функция принимает массив символов void print_message(char message[]){ printf("%s\n", message); } // прототип выглядил бы так void print_message(char[]); если параметров несколько, они перечисляются через запятую параметры ф-ии можно изменять((При компиляции функции для ее параметров выделяются отдельные участки памяти. Передаеться только значение)) в теле функции параметры могут иметь те же модификаторы, что и обычные переменные ==== Передача параметров ==== Существует два основных способа передачи параметров в функции: **по значению** и **по указателю**. При **передаче по значению** значением будет копия аргумента, а функция работает с этой копией. Это означает, что изменения, внесенные в параметры внутри функции, не отразятся на аргументах, переданных в функцию. void modify_value(int value) { value = 100; // Это изменение не затронет оригинальный аргумент } int main(void) { int original = 42; modify_value(original); printf("Original: %d\n", original); // Вывод: Original: 42 return 0; } При **передаче по указателю** передается адрес переменной, что позволяет функции изменять оригинальный аргумент. Этот подход часто используется для работы с массивами, строками и большими структурами данных, чтобы избежать лишнего расхода памяти при копировании. void modify_value(int *value) { *value = 100; // Изменяет оригинальный аргумент } int main(void) { int original = 42; modify_value(&original); printf("Original: %d\n", original); // Вывод: Original: 100 return 0; } Использование указателей также позволяет передавать массивы в функции, так как имя массива автоматически преобразуется в указатель на его первый элемент: void print_array(int array[], int size) { for (int i = 0; i < size; i++) { printf("%d ", array[i]); } printf("\n"); } int main(void) { int numbers[] = {1, 2, 3, 4, 5}; print_array(numbers, 5); // Передается адрес первого элемента массива return 0; } ==== Переменное количество параметров ==== [[stdarg.h]] ===== Возвращаемые значения ===== Функция может иметь результат - некоторое значение, которое оно возвращает во внешний код. void sum1(int x, int y){ printf("%d + %d = %d \n", x, y, x + y); } // аналогично sum1 void sum2(int x, int y){ printf("%d + %d = %d \n", x, y, x + y); return; } // возвращает int int sum3(int x, int y){ return x + y; } int result = sum(x, y); В тех функциях, которые не возвращают никакого значения в качестве возвращаемого типа используется void. Если такой ф-ии явно не указать пустой return; - компилятор сам подставит его. При вызове return функция завершаеться, даже если после return есть еще код ===== Рекурсия ===== функция может вызывать саму себя прямо в теле int factorial(int n) { if (n == 1) { return 1; } return n * factorial(n - 1); } ===== Типы функций ===== Используя определение функции - ее возвращаемый тип и типы параметров мы можем описать тип функций. Для этого применяется оператор typedef:\\ ''typedef возвращаемый_тип (название)(типы_параметров);'' #include typedef void (message)(void); void hello() { printf("Hello, World \n"); } void goodbye() { printf("Good Bye, World \n"); } int main(void) { message* mes1 = &hello; // указатель указывает на функцию hello message* mes2 = &goodbye; // указатель указывает на функцию goodbye mes1(); // Hello, World mes2(); // Good Bye, World return 0; } ==== Тип функции-указатель ==== Тип функции можно определить как указатель. Например:\\ ''typedef int (*binary_op)(int, int);'' Но стоит понимать, что в этом случае binary_op уже будет представлять указатель: #include typedef int (*binary_op)(int, int); int sum(int x, int y) { return x + y; } int main(void) { binary_op op1 = ∑ // op1 уже изначально представляет указатель printf("result = %d \n", op1(10,5)); // result=15 return 0; }