Как описать функцию в с
Перейти к содержимому

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

  • автор:

Функции

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

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

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

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

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

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

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

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

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

int main()

Возвращаемым типом функции является тип int , поэтому функция должна использовать оператор return и возвращать какое-либо значение, которое соответствует типу int. Возвращаемое значение ставится после оператора return.

Стоит отметить, что С++ позволяет не использовать оператор return в функции main:

int main()

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

void hello()

Выполнение функции

Когда запускается программа на языке C++, то запускается функция main. Никакие другие функции, определенные в программе, автоматически не выполняются. Для выполнения функции ее необходимо вызвать. Вызов функции осуществляется в форме:

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

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

Например, определим и выполним простейшую функцию:

#include void hello() < std::cout int main()

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

hello hello

Объявление функции

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

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

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

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

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

void hello();

Используем объявление функции:

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

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

Функции

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

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

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

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

Первая строка представляет заголовок или сигнатуру функции. Вначале указывается возвращаемый тип функции. Если функция не возвращает никакого значения, то используется тип 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++: Определение функций

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

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

В этом уроке мы рассмотрим как создавать пользовательские функции.

Что такое функция

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

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

Вызов функции — это выражение, которое говорит CPU прервать текущую функцию и выполнить другую. Получается, что CPU «помещает закладку» в текущую точку выполнения, а затем вызывает функцию, указанную в вызове функции. Когда вызываемая функция завершается, CPU возвращается в точку, которая отмечена закладкой, и возобновляет выполнение.

Разберемся как определять пользовательские функции в С++.

Структура функции

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

тип_возвращаемого_значения идентификатор(параметры функции) < // идентификатор заменяется именем вашей функции // ваш код здесь >

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

Рассмотрим пример программы, которая показывает, как определяется и вызывается новая функция:

void Greating() < std::cout int main() < std::cout 

Эта программа создает следующий вывод:

Starting main() Hello Code Basics! Ending main()

Программа начинает выполняться с начала функции main . Первая строка, которая будет выполняться, выводит текст Starting main() . Вторая строка в main — это вызов функции Greating() . Мы вызываем функцию Greating() , добавляя скобки к имени функции, например: Greating() . Если забыть про скобки, программа может не компилироваться.

Так как мы вызвали функцию, выполнение инструкций в main приостанавливается. В итоге выполнение переходит к началу вызываемой функции Greating() .

Первая и единственная строка в Greating() печатает текст Hello Code Basics! . Когда Greating() завершается, выполнение возвращается к вызывающей функции — здесь: функция main . Далее оно возобновляется с того места, где остановилось. Получается, следующая инструкция, которая выполняется в main , выводит на печать Ending main() .

Особенность функций

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

void Greating() < std::cout int main()

Обратите внимание мы вызываем функцию Greating() два раза.

Эта программа создает следующий вывод:

Hello Code Basics! Hello Code Basics!

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

int main() < void Greating() < std::cout Greating(); return 0; > 

В коде выше мы попытались определить внутри функции main() , функцию Greating() и получили ошибку компиляции:

main.cpp:12:3: error: ‘PrintMoto’ was not declared in this scope 12 | PrintMoto(); | ^~~~~~~~~

Это прежде всего связанно с особенностью языка и выделением памяти.

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

Задание

Напишите функцию PrintMoto() , которая выводит на экран фразу Spring is coming , и вызовите ее внутри функции main .

Упражнение не проходит проверку — что делать? ��

Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:

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

В моей среде код работает, а здесь нет ��

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя ��

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

В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно ��

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.

Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.

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

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