Общая структура функций в языке Си
Как и во многих других языках программирования, в Си имеются средства создания функций. Используются они как и в других ЯП для организации кода и повторного использования блоков операторов.
<возвращаемый тип> <имя функции>(<тип1> <арг1>, <тип1> <арг2>, ...) {
<тело функции>
}
Напишем пару функций. Глупая программа для получения числа в диапазоне от -100 до 100 с проверкой ввода и "красивым" выводом крадрата числа:
#include <stdio.h> #include <stdlib.h> int get_input() { puts("Please enter number less than 100 and more -100:"); int input; scanf("%d", &input); if (input >= 100) { printf("Expected number less than 100, but got: %d\n", input); exit(1); } if (input <= -100) { printf("Expected number greater than -100, but got: %d\n", input); exit(1); } return input; } void print_output(int value) { puts("=== output ==="); printf("%d\n", value); puts("=============="); } int calc_value(value) { return value * value; } int main(void) { int arg = get_input(); int value = calc_value(arg); print_output(value); return 0; }
– Всё, конечно, можно было оставить в main, но куда приятнее и удобнее разбить на семантически раздельные функции.
Воспользуемся кодом:
→ clang 03-simple-funcs.c → ./a.out Please enter number less than 100 and more -100: 123 Expected number less than 100, but got: 123 → ./a.out Please enter number less than 100 and more -100: -123 Expected number greater than -100, but got: -123 → ./a.out Please enter number less than 100 and more -100: 12 === output === 144 ==============
Однако, если переместить main выше определения функций, получим:
→ clang 03-simple-funcs.c
03-simple-funcs.c:6:15: warning: implicit declaration of function 'get_input' is invalid in C99 [-Wimplicit-function-declaration]
int arg = get_input();
^
03-simple-funcs.c:7:17: warning: implicit declaration of function 'calc_value' is invalid in C99 [-Wimplicit-function-declaration]
int value = calc_value(arg);
^
03-simple-funcs.c:8:5: warning: implicit declaration of function 'print_output' is invalid in C99 [-Wimplicit-function-declaration]
print_output(value);
^
03-simple-funcs.c:33:6: error: conflicting types for 'print_output'
void print_output(int value) {
^
03-simple-funcs.c:8:5: note: previous implicit declaration is here
print_output(value);
^
3 warnings and 1 error generated.
Как можно увидеть, возникли проблемы: на момент использования функций в main они ещё не определены. Всё же компилятор Си однопроходный...
Однако, мы можем описать их прототипы, чтобы при компилятор понимал: какие типы значений ожидаются функциями и какие типы значений вернут функции. Ещё немного изменим наш код:
#include <stdio.h> #include <stdlib.h> /** * Прототипы */ int get_input(); void print_output(int); int calc_value(int); int main(void) { int arg = get_input(); int value = calc_value(arg); print_output(value); return 0; } int get_input() { puts("Please enter number less than 100 and more -100:"); int input; scanf("%d", &input); if (input >= 100) { printf("Expected number less than 100, but got: %d\n", input); exit(1); } if (input <= -100) { printf("Expected number greater than -100, but got: %d\n", input); exit(1); } return input; } void print_output(int value) { puts("=== output ==="); printf("%d\n", value); puts("=============="); } int calc_value(int value) { return value * value; }
Уже было видно в предыдущем примере, как можно передавать данные в функции
// Пототипы void print_output(int); int calc_value(int); ... // main int value = calc_value(arg); print_output(value);
Сами значения копируются, а после выхода из функций удаляются (помимо return значения, которое также копируется).
Однако, мы помним, что в Си у нас всё данные, даже адреса. Поэтому мы можем передать адрес на данные и модифицировать данные внутри функции, которые будут использоваться во вне.
Вспомним уже использованную функцию scanf - форматированный ввод со стандартного ввода:
#include <stdio.h> int main(void) { int num; scanf("%d", &d); printf("%d", d); return 0; }
Нам нужен указатель на переменную в scanf, чтобы записать по адресу значение во вне. Нам не нужен указатель для printf – нам нужно получить значение, вывести его – изменять его значение вне функции не нужно.
Это важный момент в выборе API для функций.