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

Как описать функцию в с

  • автор:

Функции

Если переменные и константы хранят состояние программы, то функции определяют ее действия. По сути функция представляет именованный кусок кода, который можно многократно использовать в различных частях программы.

Определение функции

Формальное определение функции выглядит следующим образом:

тип имя_функции(параметры)

Первая строка представляет заголовок или сигнатуру функции. Вначале указывается возвращаемый тип функции. Если функция не возвращает никакого значения, то используется тип void .

Затем идет имя функции, которое представляет произвольный идентификатор, к которому применяются те же правила, что и к именованию переменных.

После имени функции в круглых скобках идет перечисление параметров. Функция может не иметь параметров, в этом случае указываются пустые скобки.

После заголовка функции в фигурных скобках идет тело функции, которое содержит выполняемые выражения.

В предыдущих статьях уже использовались как минимум две функции. Так, каждая программа на языке Си должна содержать функцию main . Также для вывода строки на консоль применялась функция printf() . Теперь определим свою простейшую функцию:

#include void hello() < printf("Hello!\n"); >int main(void)

Кроме стандартной функции main здесь также определена функция hello . Эта функция имеет тип void , то есть фактически она ничего не возвращает. Она не имеет никаких параметров, поэтому после названия функции идут пустые круглые скобки. И все, что делает данная функция, — просто выводит на консоль строку «Hello!».

Но если мы запустим сейчас программу на выполнение, то никаких строк на консоль не будет выводиться, более того наша функция hello() не будет выполняться..

Вызов функции

Для выполнения функции ее необходимо вызвать. Вызов функции осуществляется в форме:

имя_функции(аргументы);

После имени функции указываются скобки, в которых перечисляются аргументы — значения для параметров функции.

В любой программе должна быть как минимум одна функция, которая имеет фиксированное имя main() . Именно с функции main начинается выполнение. Поэтому если мы хотим выполнить функцию hello, то нам надо ее вызвать в функции main.

#include // определение функции void hello() < printf("Hello!\n"); >int main(void) < hello(); // вызов функции hello(); // вызов функции return 0; >

В данном случае функция hello вызывается два раза. В этом и состоит прелесть функция: мы можем вынести некоторое общее действие, которое может состоять из нескольких строк, в отдельную функцию и затем вызывать многократно в различных местах программы. Поскольку функция hello() не имеет никаких параметров, то в при вызове ей не передается никаких аргументов — указываются просто пустые круглые скобки:

hello();

В итоге программа два раза выведет строку «Hello».

Прототип или описание функции

Здесь стоит учесть важный момент — компилятор должен знать о функции до ее вызова. Поэтому вызов функции должен происходить после ее определения, как в случае выше. В некоторых языках это не имеет значение, но в языке Си это играет большую роль. И если, к примеру, мы сначала вызовем, а потом определим функцию, то мы можем получить ошибку на этапе компиляции, как в следующем случае:

#include int main(void) < hello(); hello(); return 0; >void hello()

Некоторые компиляторы могут скомпилировать данный код, но тем не менее при компиляции сгенерируют предупреждение.

Но есть и другой способ, который заключается в использовании описания функции. Описание функции еще называют прототипом . Формальное определение прототипа выглядит следующим образом:

тип имя_функции(параметры);

Фактически это заголовок функции. То есть для функции hello прототип будет выглядеть следующим образом:

void hello();

Применим прототип функции:

#include // описание void hello(void); int main(void) < hello(); hello(); return 0; >// определение void hello()

В данном случае несмотря на то, что определение функции идет после ее вызова, но так как ее описание идет до вызова, то компилятор уже будет знать о функции hello, и никаких проблем в работе программы не возникнет.

Стоит отметить, что если функция не принимает никаких параметров, то в ее прототипе в скобках указывается void , как в примере выше с функцией hello .

Функции (C++)

Функции — это блоки кода, выполняющие определенные операции. Если требуется, функция может определять входные параметры, позволяющие вызывающим объектам передавать ей аргументы. При необходимости функция также может возвращать значение как выходное. Функции полезны для инкапсуляции основных операций в едином блоке, который может многократно использоваться. В идеальном случае имя этого блока должно четко описывать назначение функции. Следующая функция принимает два целых числа из вызывающего средства и возвращает их сумму; a и b — это параметры типа int .

int sum(int a, int b)

Функция может вызываться или вызываться из любого количества мест в программе. Значения, передаваемые функции, являются аргументами, типы которых должны быть совместимы с типами параметров в определении функции.

int main() < int i = sum(10, 32); int j = sum(i, 66); cout 

Нет практического ограничения на длину функции, но хороший дизайн предназначен для функций, выполняющих одну хорошо определенную задачу. Сложные алгоритмы лучше разбивать на более короткие и простые для понимания функции, если это возможно.

Функции, определенные в области видимости класса, называются функциями-членами. В C++, в отличие от других языков, функции можно также определять в области видимости пространства имен (включая неявное глобальное пространство имен). Такие функции называются бесплатными или не-членными функциями; они широко используются в стандартной библиотеке.

Функции могут быть перегружены, что означает, что разные версии функции могут совместно использовать одно и то же имя, если они отличаются числом и /или типом формальных параметров. Дополнительные сведения см. в разделе "Перегрузка функций".

Части объявления функции

Минимальное объявление функции состоит из возвращаемого типа, имени функции и списка параметров (которые могут быть пустыми), а также необязательных ключевое слово, которые предоставляют дополнительные инструкции компилятору. В следующем примере представлено объявление функции:

int sum(int a, int b); 

Определение функции состоит из объявления, а также текста, который представляет собой весь код между фигурными скобками:

int sum(int a, int b)

Объявление функции, за которым следует точка с запятой, может многократно встречаться в разных местах кода программы. Оно необходимо перед любыми вызовами этой функции в каждой записи преобразования. По правилу одного определения, определение функции должно фигурировать в коде программы лишь один раз.

При объявлении функции необходимо указать:

  1. Возвращаемый тип, указывающий тип значения, возвращаемого функцией, или void если значение не возвращается. В C++11 является допустимым типом возвращаемого значения, auto который указывает компилятору выводить тип из инструкции return. В C++14 decltype(auto) также разрешено. Дополнительные сведения см. в подразделе "Выведение возвращаемых типов" ниже.
  2. Имя функции, которое должно начинаться с буквы или подчеркивания и не может содержать пробелы. Как правило, ведущие подчеркивания в именах функций стандартной библиотеки указывают на частные функции-члены или функции, не являющиеся членами, которые не предназначены для использования в коде.
  3. Список параметров, заключенный в скобки. В этом списке через запятую указывается нужное (возможно, нулевое) число параметров, задающих тип и, при необходимости, локальное имя, по которому к значениям можно получить доступ в теле функции.

Необязательные элементы объявления функции:

    constexpr — указывает, что возвращаемое значение функции является константой, значение которой может быть определено во время компиляции.

constexpr float exp(float x, int n) < return n == 0 ? 1 : n % 2 == 0 ? exp(x * x, n / 2) : exp(x * x, (n - 1) / 2) * x; >; 
//Declare printf with C linkage. extern "C" int printf( const char *fmt, . ); 
inline double Account::GetBalance()
#include template T copy_object(T& obj) noexcept(std::is_pod)

Определения функций

Определение функции состоит из объявления и текста функции, заключенного в фигурные скобки, которые содержат объявления переменных, операторы и выражения. В следующем примере показано полное определение функции:

 int foo(int i, std::string s) < int value ; MyClass mc; if(strcmp(s, "default") != 0) < value = mc.do_something(i); >return value; > 

Переменные, объявленные в теле функции, называются локальными. Они исчезают из области видимости при выходе из функции, поэтому функция никогда не должна возвращать ссылку на локальную переменную.

 MyClass& boom(int i, std::string s) < int value ; MyClass mc; mc.Initialize(i,s); return mc; > 

функции const и constexpr

Можно объявить функцию-член, чтобы const указать, что функция не может изменять значения элементов данных в классе. Объявив функцию-член как const , компилятор помогает обеспечить правильность константа. Если кто-то ошибочно пытается изменить объект с помощью функции, объявленной как const , возникает ошибка компилятора. Дополнительные сведения см. в разделе const.

Объявите функцию, как constexpr когда значение, которое он создает, может быть определено во время компиляции. Функция constexpr обычно выполняется быстрее, чем обычная функция. Дополнительные сведения см. в разделе constexpr .

Шаблоны функций

Шаблоны функций подобны шаблонам классов. Их задача заключается в создании конкретных функций на основе аргументов шаблонов. Во многих случаях шаблоны могут определять типы аргументов, поэтому их не требуется явно указывать.

template auto Add2(const Lhs& lhs, const Rhs& rhs) < return lhs + rhs; >auto a = Add2(3.13, 2.895); // a is a double auto b = Add2(string< "Hello" >, string< " World" >); // b is a std::string 

Дополнительные сведения см. в разделе "Шаблоны функций"

Параметры и аргументы функций

У функции имеется список параметров, в котором через запятую перечислено необходимое (возможно, нулевое) число типов. Каждому параметру присваивается имя, по которому к нему можно получить доступ в теле функции. Шаблон функции может указывать дополнительные параметры типа или значения. Вызывающий объект передает аргументы, представляющие собой конкретные значения, типы которых совместимы со списком параметров.

По умолчанию аргументы передаются функции по значению, то есть функция получает копию передаваемого объекта. Для больших объектов копирование может быть дорогостоящим и не всегда требуется. Чтобы привести к передаче аргументов по ссылке (в частности, ссылке lvalue), добавьте к параметру квантификатор ссылок:

void DoSomething(std::string& input)

Если функция изменяет аргумент, передаваемый по ссылке, изменяется исходный объект, а не его локальная копия. Чтобы предотвратить изменение такого аргумента функции, квалифицируйте параметр как const&:

void DoSomething(const std::string& input)

C++11. Чтобы явно обрабатывать аргументы, передаваемые rvalue-reference или lvalue-reference, используйте двойной амперсанд для параметра, чтобы указать универсальную ссылку:

void DoSomething(const std::string&& input)

Функция, объявленная с одним ключевое слово void в списке объявлений параметров, не принимает аргументов, если ключевое слово void является первым и единственным членом списка объявлений аргументов. Аргументы типа void в другом месте списка создают ошибки. Например:

// OK same as GetTickCount() long GetTickCount( void ); 

Хотя это недопустимо указывать void аргумент, кроме описанного здесь, типы, производные от типа void (например, указателей на void и массивы) могут отображаться в любом месте списка объявлений void аргументов.

Аргументы по умолчанию

Последним параметрам в сигнатуре функции можно назначить аргумент по умолчанию, т. е. вызывающий объект сможет опустить аргумент при вызове функции, если не требуется указать какое-либо другое значение.

int DoSomething(int num, string str, Allocator& alloc = defaultAllocator) < . >// OK both parameters are at end int DoSomethingElse(int num, string str = string< "Working" >, Allocator& alloc = defaultAllocator) < . >// C2548: 'DoMore': missing default parameter for parameter 2 int DoMore(int num = 5, // Not a trailing parameter! string str, Allocator& = defaultAllocator)

Дополнительные сведения см. в разделе "Аргументы по умолчанию".

типов возвращаемых функциями значений;

Функция может не возвращать другую функцию или встроенный массив; однако он может возвращать указатели на эти типы или лямбда-объект, который создает объект функции. За исключением этих случаев, функция может возвращать значение любого типа, который находится в область, или не возвращать никакого значения, в этом случае возвращаемый тип. void

Завершающие возвращаемые типы

"Обычные" возвращаемые типы расположены слева от сигнатуры функции. Конечный возвращаемый тип находится в правой части подписи и предшествует оператору -> . Завершающие возвращаемые типы особенно полезны в шаблонах функций, когда тип возвращаемого значения зависит от параметров шаблона.

template auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs)

Если auto используется в сочетании с конечным типом возвращаемого значения, он просто служит заполнителем для любого создаваемого выражения деклтипа и не выполняет вычет типов.

Локальные переменные функции

Переменная, объявленная внутри тела функции, называется локальной переменной или просто локальной . Нестатические локальные локальные параметры отображаются только внутри тела функции, и если они объявлены в стеке выходят из область при выходе функции. При создании локальной переменной и возврате ее по значению компилятор обычно может выполнять оптимизацию именованных возвращаемых значений, чтобы избежать ненужных операций копирования. Если локальная переменная возвращается по ссылке, компилятор выдаст предупреждение, поскольку любые попытки вызывающего объекта использовать эту ссылку произойдут после уничтожения локальной переменной.

В C++ локальные переменные можно объявлять как статические. Переменная является видимой только в теле функции, однако для всех экземпляров функции существует только одна копия переменной. Локальные статические объекты удаляются во время завершения, определенного директивой atexit . Если статический объект не был создан, так как поток управления программы обошел его объявление, попытка уничтожить этот объект не выполняется.

Вычет типов в возвращаемых типах (C++14)

В C++14 можно использовать auto для указания компилятору выводить тип возвращаемого значения из тела функции, не предоставляя конечный тип возвращаемого значения. Обратите внимание, что auto всегда дедуцирует возвращаемое значение. Используйте auto&& , чтобы дать компилятору команду выведения ссылки.

В этом примере auto будет выведено в виде копии неконстантных значений суммы lhs и rhs.

template auto Add2(const Lhs& lhs, const Rhs& rhs) < return lhs + rhs; //returns a non-const object by value >

Обратите внимание, что auto не сохраняет константность типа, который он выводит. Для переадресации функций, возвращаемые значением которых необходимо сохранить констант-ness или ref-ness его аргументов, можно использовать decltype(auto) ключевое слово, которая использует decltype правила вывода типов и сохраняет все сведения о типе. decltype(auto) может использоваться в качестве обычного возвращаемого значения слева или в качестве возвращаемого значения.

В следующем примере (на основе кода из N3493) показано decltype(auto) , что используется для обеспечения идеальной пересылки аргументов функций в возвращаемом типе, который не известен до создания шаблона.

template, int. I> decltype(auto) apply_(F&& f, Tuple&& args, index_sequence) < return std::forward(f)(std::get(std::forward(args)). ); > template, typename Indices = make_index_sequence> decltype( auto) apply(F&& f, Tuple&& args) < return apply_(std::forward(f), std::forward(args), Indices()); > 

Возврат нескольких значений из функции

Существует несколько способов возврата нескольких значений из функции:

    Инкапсулируйте значения в именованном классе или объекте структуры. Требуется, чтобы определение класса или структуры отображалось вызывающей объекту:

#include #include using namespace std; struct S < string name; int num; >; S g() < string t< "hello" >; int u< 42 >; return < t, u >; > int main()
#include #include #include using namespace std; tuple f() < int i< 108 >; string s< "Some text" >; double d< .01 >; return < i,s,d >; > int main() < auto t = f(); cout (t) (t) (t) 
#include #include #include using namespace std; tuple f() < int i< 108 >; string s< "Some text" >; double d< .01 >; return < i,s,d >; > struct S < string name; int num; >; S g() < string t< "hello" >; int u< 42 >; return < t, u >; > int main() < auto[x, y, z] = f(); // init from tuple cout 

Указатели функций

Как и в C, в C++ поддерживаются указатели на функции. Однако более типобезопасной альтернативой обычно служит использование объекта-функции.

Рекомендуется typedef объявить псевдоним для типа указателя функции при объявлении функции, возвращающей тип указателя функции. Например.

typedef int (*fp)(int); fp myFunction(char* s); // function returning function pointer 

Если это не сделано, правильный синтаксис объявления функции может быть выведен из синтаксиса декларатора для указателя функции, заменив идентификатор ( fp в приведенном выше примере) именем функций и списком аргументов следующим образом:

int (*myFunction(char* s))(int); 

Предыдущее объявление эквивалентно объявлению, используемому typedef ранее.

Функции в C++ — урок 6

Сегодня мы поговорим о функциях в C++. Очень часто в программировании необходимо выполнять одни и те же действия. Например, мы хотим выводить пользователю сообщения об ошибке в разных местах программы, если он ввел неверное значение. без функций это выглядело бы так:

#include #include using namespace std; int main() < string valid_pass = "qwerty123"; string user_pass; cout else < cout return 0; > 

А вот аналогичный пример с функцией:

#include #include using namespace std; void check_pass (string password) < string valid_pass = "qwerty123"; if (password == valid_pass) < cout else < cout > int main()

По сути, после компиляции не будет никакой разницы для процессора, как для первого кода, так и для второго. Но ведь такую проверку пароля мы можем делать в нашей программе довольно много раз. И тогда получается копипаста и код становится нечитаемым. Функции — один из самых важных компонентов языка C++.

  • Любая функция имеет тип, также, как и любая переменная.
  • Функция может возвращать значение, тип которого в большинстве случаев аналогично типу самой функции.
  • Если функция не возвращает никакого значения, то она должна иметь тип void (такие функции иногда называют процедурами)
  • При объявлении функции, после ее типа должно находиться имя функции и две круглые скобки — открывающая и закрывающая, внутри которых могут находиться один или несколько аргументов функции, которых также может не быть вообще.
  • после списка аргументов функции ставится открывающая фигурная скобка, после которой находится само тело функции.
  • В конце тела функции обязательно ставится закрывающая фигурная скобка.

Пример построения функции

#include using namespace std; void function_name () < cout int main() < function_name(); // Вызов функции return 0; >

Перед вами тривиальная программа, Hello, world, только реализованная с использованием функций.

Если мы хотим вывести «Hello, world» где-то еще, нам просто нужно вызвать соответствующую функцию. В данном случае это делается так: function_name(); . Вызов функции имеет вид имени функции с последующими круглыми скобками. Эти скобки могут быть пустыми, если функция не имеет аргументов. Если же аргументы в самой функции есть, их необходимо указать в круглых скобках.

Также существует такое понятие, как параметры функции по умолчанию. Такие параметры можно не указывать при вызове функции, т.к. они примут значение по умолчанию, указанно после знака присваивания после данного параметра и списке всех параметров функции.

В предыдущих примерах мы использовали функции типа void , которые не возвращают никакого значения. Как многие уже догадались, оператор return используется для возвращения вычисляемого функцией значения.

Рассмотрим пример функции, возвращающей значение на примере проверки пароля.

#include #include using namespace std; string check_pass (string password) < string valid_pass = "qwerty123"; string error_message; if (password == valid_pass) < error_message = "Доступ разрешен."; >else < error_message = "Неверный пароль!"; >return error_message; > int main()

В данном случае функция check_pass имеет тип string, следовательно она будет возвращать только значение типа string, иными словами говоря строку. Давайте рассмотрим алгоритм работы этой программы.

Самой первой выполняется функция main(), которая должна присутствовать в каждой программе. Теперь мы объявляем переменную user_pass типа string, затем выводим пользователю сообщение «Введите пароль», который после ввода попадает в строку user_pass. А вот дальше начинает работать наша собственная функция check_pass() .

В качестве аргумента этой функции передается строка, введенная пользователем.

Аргумент функции — это, если сказать простым языком переменные или константы вызывающей функции, которые будет использовать вызываемая функция.

При объявлении функций создается формальный параметр, имя которого может отличаться от параметра, передаваемого при вызове этой функции. Но типы формальных параметров и передаваемых функии аргументов в большинстве случаев должны быть аналогичны.

После того, как произошел вызов функции check_pass() , начинает работать данная функция. Если функцию нигде не вызвать, то этот код будет проигнорирован программой. Итак, мы передали в качестве аргумента строку, которую ввел пользователь.

Теперь эта строка в полном распоряжении функции (хочу обратить Ваше внимание на то, что переменные и константы, объявленные в разных функциях независимы друг от друга, они даже могут иметь одинаковые имена. В следующих уроках я расскажу о том, что такое область видимости, локальные и глобальные переменные).

Теперь мы проверяем, правильный ли пароль ввел пользователь или нет. если пользователь ввел правильный пароль, присваиваем переменной error_message соответствующее значение. если нет, то сообщение об ошибке.

После этой проверки мы возвращаем переменную error_message . На этом работа нашей функции закончена. А теперь, в функции main(), то значение, которое возвратила наша функция мы присваиваем переменной error_msg и выводим это значение (строку) на экран терминала.

Также, можно организовать повторный ввод пароля с помощью рекурсии (о ней мы еще поговорим). Если объяснять вкратце, рекурсия — это когда функция вызывает сама себя. Смотрите еще один пример:

#include #include using namespace std; bool password_is_valid (string password) < string valid_pass = "qwerty123"; if (valid_pass == password) return true; else return false; >void get_pass () < string user_pass; cout else < cout > int main()

Функции очень сильно облегчают работу программисту и намного повышают читаемость и понятность кода, в том числе и для самого разработчика (не удивляйтесь этому, т. к. если вы откроете код, написанный вами полгода назад,не сразу поймете соль, поверьте на слово).

Не расстраивайтесь, если не сразу поймете все аспекты функций в C++, т. к. это довольно сложная тема и мы еще будем разбирать примеры с функциями в следующих уроках.

Совет: не бойтесь экспериментировать, это очень хорошая практика, а после прочтения данной статьи порешайте элементарные задачи, но с использованием функций. Это будет очень полезно для вас.

Если Вы найдете какие-либо ошибки в моем коде, обязательно напишите об этом в комментариях. здесь же можно задавать все вопросы.

Объявление и описание функций в C++ Builder

Запись была обновлена

Функции представляют собой программные блоки, которые могут вызываться из разных частей программы. При вызове в них передаются некоторые переменные, константы, выражения, являющиеся аргументами, которые в самих процедурах и функциях воспринимаются как формальные параметры. При этом функции возвращают значение определенного типа, которое замещает в вызвавшем выражении имя вызванной функции. Например, оператор:

I = 5*F(X);

вызывает функцию Fс аргументом X, умножает возвращенное ею значение на 5 и присваивает результат переменной I.

Допускается также вызов функции, не использующий возвращаемого ею значения. Например:

В этом случае возвращаемое функцией значение игнорируется. Функция описывается следующим образом:

тип_возвращаемого_значения имя_функции(список_параметров)

Первая строка этого описания, содержащая тип возвращаемого значения, имя функции и список параметров, называется заголовком функции. Тип возвращаемого значения может быть любым, кроме массива и функции. Могут быть также функции, не возвращающие никакого значения. В заголовке таких функций тип возвращаемого значения объявляется void.

Если тип возвращаемого значения не указан, он по умолчанию считается равным int.

Хотя тип возвращаемого значения int можно не указывать в заголовке функции, не следует использовать эту возможность. Всегда указывайте тип возвращаемого значения, кроме главной функции main. Указание типа делает программу более наглядной и предотвращает возможные ошибки, связанные с неправильным преобразованием типов.

Список параметров, заключаемый в скобки, в простейшем случае (более сложные формы задания списка параметров будут рассмотрены позднее) представляет собой разделяемый запятыми список вида:

тип_параметра идентификатор_параметра
double FSum(double X1, double X2, int h)

объявляет функцию с именем FSum, с тремя параметрами X1, X2 и А, из которых первые два имеют тип double, а последний int. Тип возвращаемого результата - double. Имена параметров X1, X2 и А локальные, т.е. они имеют значение только внутри данной функции и никак не связаны с именами аргументов, переданных при вызове функции. Значения этих параметров в начале выполнения функции равны значениям аргументов на момент вызова функции.

Ниже приведен заголовок функции, не возвращающей никакого значения:

void SPrint(AnsiString S)

Она принимает один параметр типа строки и, например, отображает его в каком-нибудь окне приложения.

Если функция не принимает никаких параметров, то скобки или оставляются пустыми, или в них записывается ключевое слово void. Например:

void F1(void)
void F1()

Всегда указывайте void в списке параметров, если функция не получает никаких параметров. Эта делает программу более переносимой.

Роль пустого списка параметров функции в С++ существенно отличается от аналогичного списка в языке С. В С это означает, что все проверки аргументов отсутствуют (т.е. вызов функции может передать любой аргумент, который требуется). А в С++ пустой список означает отсутствие аргументов. Таким образом, программа на С, использующая эту особенность, может сообщить о синтаксической ошибке при компиляции в С++.

Как правило (хотя формально не обязательно), помимо описания функции в текст программы включается также прототип функции ее предварительное объявление. Прототип представляет собой тот же заголовок функции, но с точкой с запятой ";" в конце. Кроме того, в прототипе можно не указывать имена параметров. Если вы все-таки указываете имена, то их областью действия является только этот прототип функции. Вы можете использовать те же идентификаторы в любом месте программы в любом качестве. Таким образом, указание имен параметров в прототипе обычно преследует только одну цель - документирование программы, напоминание вам или сопровождающему программу человеку, какой параметр что именно обозначает.

Примеры прототипов приведенных выше заголовков функций:

double FSum(double X1,double X2, int A); void SPrint(AnsiString S); void F1(void);
double FSum(double, double, int); void SPrint(AnsiString); void F1();

Введение в программу прототипов функций преследует несколько целей. Во-первых, это позволяет использовать в данном модуле функцию, описанную в каком-нибудь другом модуле. Тогда из прототипа компилятор получает сведения, сколько параметров, какого типа и в какой последовательности получает данная функция. Во-вторых, если в начале модуля вы определили прототипы функций, то последовательность размещения в модуле описания функций безразлична. При отсутствии прототипов любая используемая функция должна быть описана до ее первого вызова в тексте. Это прибавляет вам хлопот, а иногда при взаимных вызовах функций друг из друга вообще невозможно. И, наконец, прототипы, размещенные в одном месте (обычно в начале модуля), делают программу более наглядной и самодокументированной. Особенно в случае, если вы снабжаете прототипы хотя бы краткими комментариями.

Если предполагается, что какие-то из описанных в модуле функций могут использоваться в других модулях, прототипы этих функций следует включать в заголовочный файл. Тогда в модулях, использующих данные функции, достаточно будет написать директиву #include, включающую данный заголовочный файл, и не надо будет повторять прототипы функций.

Включайте в модуль где-то в одном месте (обычно в начале) прототипы всех описанных в нем ваших функций с краткими комментариями. Это хорошо документирует программу, делает ее нагляднее, позволяет вам не заботиться о последовательности описаний функций. Бели вы хотите, чтобы какие-то из описанных в модуле функций могли использовать другие модули, включайте прототипы этих функций в заголовочный файл.

Обычно функции принимают указанное в прототипе число параметров указанных типов. Однако могут быть функции, принимающие различное число параметров (например, библиотечная функция printf) или параметры неопределенных заранее типов. В этом случае в прототипе вместо неизвестного числа параметров или вместо параметров неизвестного типа ставится многоточие ". ". Многоточие может помещаться только в конце списка параметров после известного числа параметров известного типа или полностью заменять список параметров. Например:

int prf(char* format, . );

Функция с подобным прототипом принимает один параметр format типа char* (например, строку форматирования) и произвольное число параметров произвольного типа. Функция с прототипом:

void Fp(. );

может принимать произвольное число параметров произвольного типа.

Если в прототипе встречается многоточие, то типы соответствующих параметров и их количество компилятором не проверяются.

Объявлению функции могут предшествовать спецификаторы класса памяти extern или static. Спецификатор extern предполагается по умолчанию, так что записывать его не имеет смысла. К функциям, объявленным как extern, можно получить доступ из других модулей программы. Если же объявить функцию со спецификатором static, например:

static void F(void);

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

Теперь рассмотрим описание тела функции. Тело функции пишется по тем же правилам, что и любой код программы, и может содержать объявления типов, констант, переменных и любые выполняемые операторы. Не допускается объявление и описание в теле других функций. Таким образом, функции не могут быть вложены друг в друга.

Надо иметь в виду, что все объявления в теле функции носят локальный характер. Объявленные переменные доступны только внутри данной функции. Если их идентификаторы совпадают с идентификаторами каких-то глобальных переменных модуля, то эти внешние переменные становятся невидимыми и недоступными. В этих случаях получить доступ к глобальной переменной можно, поставив перед ее именем два двоеточия "::", т.е. применив унарную операцию разрешения области действия.

Локальные переменные не просто видны только в теле функции, но по умолчанию они и существуют только внутри функции, создаваясь в момент вызова функции и уничтожаясь в момент выхода из функции. Если требуется этого избежать, соответствующие переменные должны объявляться со спецификацией static.

Выход на функции может осуществляться следующими способами. Если функции по должна возвращать никакого значения, то выход из нее происходит или по достижении закрывающей ее тело фигурной скобки, или при выполнении оператора return. Если же функция должна возвращать некоторое значение, то нормальный выход из нее осуществляется оператором return выражение, где выражение должно формировать возвращаемое значение и соответствовать типу, объявленному в заголовке функции. Например:

double FSum(double X1,double X2, int A)

Ниже приведен пример функции, не возвращающей никакого значения:

void SPrint(AnsiString S)

Здесь возврат из функции происходит по достижении закрывающейся фигурной скобки тела функции. Приведем вариант той же функции, использующий оператор return:

void SPrint(AnsiString S)

Прервать выполнение функции можно также генерацией какого-то исключения. Наиболее часто в этих целях используется процедура Abort, генерирующая «молчаливое» исключение EAbort, не связанное с каким-то сообщением об ошибке. Если в программе не предусмотрен перехват этого исключения, то применение функции Abort выводит управление сразу наверх из всех вложенных друг в друга вызовов функций.

Возвращаемое функцией значение может включать в себя вызов каких-то функций. В том числе функция может вызывать и саму себя, т.е. допускается рекурсия. В качестве примера приведем функцию, рекурсивно вычисляющую факториал. Как известно, значение факториала равно n! = n (n-1) (n-2) . 1, причем считается, что 1! = 1 и 0! = 1. Факториал можно вычислить с помощью простого цикла for (и это, конечно, проще). Но можно факториал вычислять и с помощью рекуррентного соотношения n! = n(n-1)!. Для иллюстрации рекурсии воспользуемся именно этим соотношением. Тогда функция factorial вычисления факториала может быть описана следующим образом:

unsigned long factorial (unsigned long n)

Если значение параметра n равно 0 или 1, то функция возвращает значение 1. В противном случае функция умножает текущее значение n на результат, возвращаемый вызовом той же функции factorial, но со значением параметра n, уменьшенным на единицу. Поскольку при каждом вызове значение параметра уменьшается, рано или поздно оно станет равно 1. После этого цепочка рекурсивных вызовов начнет свертываться и в конце концов вернет значение факториала.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *