Определение и вызов функций
Порядок и типы формальных параметров должны быть одинаковыми в определении функции и во
всех ее объявлениях. Типы фактических параметров при вызове функции должны быть
совместимы с типами соответствующих формальных параметров. Тип формального параметра
может быть любым основным типом, структурой, объединением, перечислением, указателем
или массивом. Если тип формального параметра не указан, то этому параметру присваивается
тип int.
Для формального параметра можно задавать класс памяти register, при этом для величин
типа int спецификатор типа можно опустить.
Идентификаторы формальных параметров используются в теле функции в качестве ссылок на
переданные значения. Эти идентификаторы не могут быть переопределены в блоке, образующем
тело функции, но могут быть переопределены во внутреннем блоке внутри тела функции.
При передаче параметров в функцию, если необходимо, выполняются обычные арифметические
преобразования для каждого формального параметра и каждого фактического параметра
независимо. После преобразования формальный параметр не может быть короче чем int, т.е.
объявление формального параметра с типом char равносильно его объявлению с типом int.
А параметры, представляющие собой действительные числа, имеют тип double.
Преобразованный тип каждого формального параметра определяет, как интерпретируются
аргументы, помещаемые при вызове функции в стек. Несоответствие типов фактических
аргументов и формальных параметров может быть причиной неверной интерпретации.
Тело функции - это составной оператор, содержащий операторы, определяющие действие
функции.
Все переменные, объявленные в теле функции без указания класса памяти, имеют класс
памяти auto, т.е. они являются локальными. При вызове функции локальным переменным
отводится память в стеке и производится их инициализация. Управление передается первому
оператору тела функции и начинается выполнение функции, которое продолжается до тех пор,
пока не встретится оператор return или последний оператор тела функции. Управление при
этом возвращается в точку, следующую за точкой вызова, а локальные переменные становятся
недоступными. При новом вызове функции для локальных переменных память распределяется
вновь, и поэтому старые значения локальных переменных теряются.
Параметры функции передаются по значению и могут рассматриваться как локальные переменные,
для которых выделяется память при вызове функции и производится инициализация значениями
фактических параметров. При выходе из функции значения этих переменных теряются.
Поскольку передача параметров происходит по значению, в теле функции нельзя изменить
значения переменных в вызывающей функции, являющихся фактическими параметрами. Однако,
если в качестве параметра передать указатель на некоторую переменную, то используя
операцию разадресации можно изменить значение этой переменной.
Пример:
/* Неправильное использование параметров */
void change (int x, int y)
{ int k=x;
x=y;
y=k;
}
В данной функции значения переменных x и y, являющихся формальными параметрами, меняются
местами, но поскольку эти переменные существуют только внутри функции change, значения
фактических параметров, используемых при вызове функции, останутся неизменными.
Для того чтобы менялись местами значения фактических аргументов можно использовать
функцию приведенную в следующем примере.
Пример:
/* Правильное использование параметров */
void change (int *x, int *y)
{ int k=*x;
*x=*y;
*y=k;
}
При вызове такой функции в качестве фактических параметров должны быть использованы не
значения переменных, а их адреса
change (&a,&b);
Если требуется вызвать функцию до ее определения в рассматриваемом файле, или
определение функции находится в другом исходном файле, то вызов функции следует
предварять объявлением этой функции. Объявление (прототип) функции имеет следующий
формат:
[спецификатор-класса-памяти] [спецификатор-типа] имя-функции ([список-формальных-
параметров]) [,список-имен-функций];
В отличие от определения функции, в прототипе за заголовком сразу же следует точка с
запятой, а тело функции отсутствует. Если несколько разных функций возвращают значения
одинакового типа и имеют одинаковые списки формальных параметров, то эти функции можно
объявить в одном прототипе, указав имя одной из функций в качестве имени-функции, а все
другие поместить в список-имен-функций, причем каждая функция должна сопровождаться
списком формальных параметров. Правила использования остальных элементов формата такие
же, как при определении функции. Имена формальных параметров при объявлении функции
можно не указывать, а если они указаны, то их область действия распространяется только
до конца объявления.
Прототип - это явное объявление функции, которое предшествует определению функции. Тип
возвращаемого значения при объявлении функции должен соответствовать типу возвращаемого
значения в определении функции.
Если прототип функции не задан, а встретился вызов функции, то строится неявный прототип
из анализа формы вызова функции. Тип возвращаемого значения создаваемого прототипа int,
а список типов и числа параметров функции формируется на основании типов и числа
фактических параметров используемых при данном вызове.
Таким образом, прототип функции необходимо задавать в следующих случаях:
1. Функция возвращает значение типа, отличного от int.
2. Требуется проинициализировать некоторый указатель на функцию до того, как эта функция
будет определена.
Наличие в прототипе полного списка типов аргументов параметров позволяет выполнить
проверку соответствия типов фактических параметров при вызове функции типам формальных
параметров, и, если необходимо, выполнить соответствующие преобразования.
В прототипе можно указать, что число параметров функции переменно, или что функция не
имеет параметров.
Если прототип задан с классом памяти static, то и определение функции должно иметь класс
памяти static. Если спецификатор класса памяти не указан, то подразумевается класс
памяти extern.