Объявление и определение функций
Язык C как и большинство других языков программирования позволяет создавать программы, состоящие из множества функций, а также из одного или нескольких файлов исходного кода. До сих пор мы видели только функцию main(), которая является главной в программе на C, поскольку выполнение кода всегда начинается с нее. Однако ничего не мешает создавать другие функции, которые могут быть вызваны из main() или любой другой функции. В этом уроке мы рассмотрим создание только однофайловых программ, содержащих более чем одну функцию.
При изучении работы функций важно понимать, что такое локальная и что такое глобальная переменные. В языке программирования C глобальные (внешние) переменные объявляются вне какой-либо функции. С их помощью удобно организовывать обмен данными между функциями, однако это считается дурным тоном, т.к. легко запутывает программу. Локальные переменные в языке программирования C называют автоматическими. Область действия автоматических переменных распространяется только на ту функцию, в которой они были объявлены. Параметры функции также являются локальными переменными.
Структурная организация файла программы на языке C, содержащего несколько функций, может выглядеть немного по-разному. Так как выполнение начинается с main(), то ей должны быть известны спецификации (имена, количество и тип параметров, тип возвращаемого значения) всех функций, которые из нее вызываются. Отсюда следует, что объявляться функции должны до того, как будут вызваны. А вот определение функции уже может следовать и до и после main(). Рассмотрим такую программу:
#include <stdio.h>
// объявление функции
float median (int a, int b);
int main () {
int num1 = 18, num2 = 35;
float result;
printf("%10.1f\n", median(num1, num2));
result = median(121, 346);
printf("%10.1f\n", result);
printf("%10.1f\n", median(1032, 1896));
}
// определение функции
float median (int n1, int n2) {
float m;
m = (float) (n1 + n2) / 2;
return m;
}
В данном случае в начале программы объявляется функция median(). Объявляются тип возвращаемого ею значения (float), количество и типы параметров (int a, int b). Обратите внимание, когда объявляются переменные, то их можно группировать: int a, b;. Однако с параметрами функций так делать нельзя, для каждого параметра тип указывается отдельно: (inta, int b).
Далее идет функция main(), а после нее — определение median(). Имена переменных-параметров в объявлении функции никакой роли не играют (их вообще можно опустить, например, float median (int, int);). Поэтому когда функция определяется, то имена параметров могут быть другими, однако тип и количество должны строго совпадать с объявлением.
Функция median() возвращает число типа float. Оператор return возвращает результат выполнения переданного ему выражения; после return функция завершает свое выполнение, даже если далее тело функции имеет продолжение. Функция median() вычисляет среднее значение от двух целых чисел. В выражении (float) (n1 + n2) / 2 сначала вычисляется сумма двух целых чисел, результат преобразуется в вещественное число и только после этого делится на 2. Иначе мы бы делили целое на целое и получили целое (в таком случае дробная часть просто усекается).
В теле main() функция median() вызывается три раза. Результат выполнения функции не обязательно должен быть присвоен переменной.
Вышеописанную программу можно было бы записать так:
#include <stdio.h>
float median (int n1, int n2) {
float m;
m = (float) (n1 + n2) / 2;
return m;
}
int main () {
int num1 = 18, num2 = 35;
float result;
printf("%10.1f\n", median(num1, num2));
result = median(121, 346);
printf("%10.1f\n", result);
printf("%10.1f\n", median(1032, 1896));
}
Хотя такой способ и экономит одну строчку кода, однако главная функция, в которой отражена основная логика программы, опускается вниз, что может быть неудобно. Поэтому первый вариант предпочтительней.
Напишите функцию, возводящую куб числа, переданного ей в качестве аргумента. Вызовите эту функцию с разными аргументами.
Статические переменные
В языке программирования C существуют так называемые статические переменные. Они могут быть как глобальными, так и локальными. Перед именем статической переменной пишется ключевое слово static.
Внешние статические переменные, в отличие от обычных глобальных переменных, нельзя использовать из других файлов в случае программы, состоящей не из одного файла. Они глобальны только для функций того файла, в котором объявлены. Это своего рода сокрытие данных, по принципу “не выставлять наружу ничего лишнего, чтобы ‘что-нибудь’ нечаянно не могло ‘испортить’ данные”.
Статические переменные, объявленные внутри функций имеют такую же область действия, как автоматические. Однако в отличие от автоматических, значения локальных статических переменных не теряются, а сохраняются между вызовами функции:
#include <stdio.h>
int hello();
int main() {
printf(" - %d-й вызов\n", hello());
printf(" - %d-й вызов\n", hello());
printf(" - %d-й вызов\n", hello());
}
int hello () {
static count = 1;
printf("Hello world!");
return count++;
}
Результат:
Hello world! - 1-й вызов Hello world! - 2-й вызов Hello world! - 3-й вызов
В этом примере в функции hello() производится подсчет ее вызовов.
Передача аргументов по ссылке
В первом примере этого урока мы передавали в функцию аргументы по значению. Это значит, что когда функция вызывается, ей передаются в качестве фактических параметров (аргументов) не указанные переменные, а копии значений этих переменных. Сами переменные к этим копиям уже никакого отношения не имеют. В вызываемой функции эти значения присваиваются переменным-параметрам, которые, как известно, локальны. Отсюда следует, что изменение переданных значений никакого влияния на переменные, переданные в функцию при вызове, не оказывают. В примере выше даже если бы в функции median() менялись значения переменных n1 и n2, то никакого влияния сей факт на переменные num1 и num2 не оказал.
Однако можно организовать изменение локальной переменной одной функции с помощью другой функции. Сделать это можно, передав в функцию адрес переменной или указатель на нее. На самом деле в этом случае также передается копия значения. Но какого значения?! Это адрес на область памяти. На один и тот же участок памяти может существовать множество ссылок, и с помощью каждой из них можно поменять находящееся там значение. Рассмотрим пример:
#include <stdio.h>
void multi (int *px, int y);
int main () {
int x = 34, y = 6;
multi(&x, 367);
multi(&y, 91);
printf("%d %d\n", x, y);
}
void multi (int *base, int pow) {
while (pow >= 10) {
*base = *base * 10;
pow = pow / 10;
}
}
Функция multi() ничего не возвращает, что подчеркнуто с помощью ключевого слова void. Принимает эта функция адрес, который присваивается локальной переменной-указателю, и целое число. В теле функции происходит изменение значения по адресу, содержащемуся в указателе. Но по сути это адрес переменной x из фукнции main(), а значит меняется и ее значение.
Когда multi() вызывается в main(), то в качестве первого параметра мы должны передать адрес, а не значение. Поэтому, например, вызов multi(x, 786) привел бы к ошибке, а вызов multi(&x, 786) — правильный, т.к. мы берем адрес переменной x и передаем его в функцию. При этом ничего не мешает объявить в main() указатель и передавать именно его (в данном случае сама переменная p содержит адрес):
int x = 34, y = 6;
int *p;
p = &x;
multi(p, 367);
p = &y;
multi(p, 367);
printf("%d %d\n", x, y);
Кроме того, следует знать, что функция может возвращать адрес.
Важно понять механизм так называемой передачи аргументов по ссылке, т.к. это понимание пригодится при изучении массивов и строк. Использовать указатели при работе с простыми типами данных не стоит. Лучше возвращать из функции значение, чем менять локальные переменные одной функции с помощью кода другой функции. Функции должны быть достаточно автономными.
- Перепишите код первого примера этого урока так, чтобы в нем использовался указатель; а код примера с функцией
multi(), наоборот, избавьте от указателей. - Напишите программу, в которой помимо функции
main()были бы еще две функции: в одной вычислялся факториал переданного числа, в другой — находился n-ый элемент ряда Фибоначчи (n — параметр функции). Вызовите эти функции с разными аргументами. - Придумайте и напишите программу, в которой из функции
main()вызывается другая функция, а из последней вызывается еще одна функция.
Источник:https://younglinux.info/