Функции (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)
Объявление функции, за которым следует точка с запятой, может многократно встречаться в разных местах кода программы. Оно необходимо перед любыми вызовами этой функции в каждой записи преобразования. По правилу одного определения, определение функции должно фигурировать в коде программы лишь один раз.
При объявлении функции необходимо указать:
- Возвращаемый тип, указывающий тип значения, возвращаемого функцией, или void если значение не возвращается. В C++11 является допустимым типом возвращаемого значения, auto который указывает компилятору выводить тип из инструкции return. В C++14 decltype(auto) также разрешено. Дополнительные сведения см. в подразделе "Выведение возвращаемых типов" ниже.
- Имя функции, которое должно начинаться с буквы или подчеркивания и не может содержать пробелы. Как правило, ведущие подчеркивания в именах функций стандартной библиотеки указывают на частные функции-члены или функции, не являющиеся членами, которые не предназначены для использования в коде.
- Список параметров, заключенный в скобки. В этом списке через запятую указывается нужное (возможно, нулевое) число параметров, задающих тип и, при необходимости, локальное имя, по которому к значениям можно получить доступ в теле функции.
Необязательные элементы объявления функции:
-
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. Передача аргументов по значению и по ссылке
Язык C как и большинство других языков программирования позволяет создавать программы, состоящие из множества функций, а также из одного или нескольких файлов исходного кода. До сих пор мы видели только функцию main , которая является главной в программе на C , поскольку выполнение кода всегда начинается с нее. Однако ничего не мешает создавать другие функции, которые могут быть вызваны из main или любой другой функции. В этом уроке мы рассмотрим создание только однофайловых программ, содержащих более чем одну функцию.
При изучении работы функций важно понимать, что такое локальная и что такое глобальная переменные. В языке программирования C глобальные (внешние) переменные объявляются вне какой-либо функции. С их помощью удобно организовывать обмен данными между функциями, однако это считается дурным тоном, т.к. легко запутывает программу. Локальные переменные в Си называют автоматическими. Область действия автоматических переменных распространяется только на ту функцию, в которой они были объявлены. Параметры функции также являются локальными переменными.
Структурная организация файла, содержащего несколько функций, может выглядеть немного по-разному. Так как выполнение начинается с main() , то ей должны быть известны спецификации (имена, количество и тип параметров, тип возвращаемого значения) всех функций, которые из нее вызываются. Отсюда следует, что объявляться функции должны до того, как будут вызваны. А вот определение функции уже может следовать и до и после main() . Рассмотрим такую программу:
#include // объявление функции 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; . Однако с параметрами функций так делать нельзя, для каждого параметра тип указывается отдельно: (int a, int b) .
Далее идет функция main , а после нее ‒ определение median . Имена переменных-параметров в объявлении функции никакой роли не играют. Их вообще можно опустить, например, float median (int, int); . Поэтому когда функция определяется, то имена параметров могут быть другими, однако тип и количество должны строго совпадать с объявлением.
Функция median() возвращает число типа float . Оператор return возвращает результат выполнения переданного ему выражения; после return функция завершает свое выполнение, даже если далее тело функции имеет продолжение. Функция median() вычисляет среднее значение от двух целых чисел. В выражении (float) (n1 + n2) / 2 сначала вычисляется сумма двух целых чисел, результат преобразуется в вещественное число и только после этого делится на 2. Иначе мы бы делили целое на целое и получили целое (в таком случае дробная часть просто усекается).
В теле main функция median() вызывается три раза. Результат выполнения функции не обязательно должен быть присвоен переменной.
Эту же программу можно написать и так:
#include 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 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 void epow(int *, int); int main() { int x = 34, y = 6; epow(&x, 3); epow(&y, 1); printf("%d %d\n", x, y); // 34000 60 } void epow(int *base, int pow) { while (pow > 0) { *base = *base * 10; pow--; } }Функция epow ничего не возвращает, о чем говорит ключевое слово void . Принимает эта функция адрес, который присваивается локальной переменной-указателю, и целое число. В теле функции происходит изменение значения по адресу, содержащемуся в указателе. Поскольку это адрес то переменной x , то y из функции main , то epow() меняет их значение.
Когда epow() вызывается в main , то в качестве первого параметра мы должны передать адрес, а не значение. Поэтому, например, вызов epow(x, 3) привел бы к ошибке, а вызов epow(&x, 3) ‒ правильный, т. к. мы берем адрес переменной x и передаем его в функцию. При этом ничего не мешает объявить в main указатель и передавать именно его (в данном случае сама переменная p содержит адрес):
int x = 34, y = 6; int *p; p = &x; epow(p, 3); p = &y; epow(p, 1); printf("%d %d\n", x, y);Кроме того, следует знать, что функция может возвращать адрес.
Важно понять механизм так называемой передачи аргументов по ссылке, т.к. это понимание пригодится при изучении массивов и строк. Использовать указатели при работе с простыми типами данных не стоит. Лучше возвращать из функции значение, чем менять локальные переменные одной функции с помощью кода другой. Функции должны быть достаточно автономными.
Напишите программу, в которой помимо функции main были бы еще две функции: в одной вычислялся факториал переданного числа, в другой ‒ находился n-ый элемент ряда Фибоначчи ( n ‒ параметр функции). Вызовите эти функции с разными аргументами.
Курс с решением задач:
pdf-версияФункции
FT
Тип объекта для вызова. Например, тип функции, объекта функции, указателя или указателя функции-члена.RTy
Тип возвращаемого значения. При указании он будет возвращаемым типом привязанного вызова. В противном случае возвращаемый тип является возвращаемым типом FT .TN
Тип N-го аргумента вызова.fn
Объект для вызова.tN
N-й аргумент вызова.Замечания
Типы FT, T1, T2, . TN должны быть конструктором копирования и INVOKE(fn, t1, . tN) должны быть допустимым выражением для некоторых значений w1, w2, . wN .
Первая шаблонная функция возвращает пересылающую оболочку вызова g со слабым типом результата. Результатом g(u1, u2, . uM) является INVOKE(f, v1, v2, . vN, invoke_result ::type) , где cv квалификаторы g cv и значения и типы связанных аргументов v1, v2, . vN определяются, как указано ниже. Его можно использовать для привязки к вызываемому объекту, чтобы создать вызываемый объект с адаптированным списком аргументов.
Вторая шаблонная функция возвращает пересылающую оболочку вызова g с вложенным типом result_type , который является синонимом для RTy . Эффект g(u1, u2, . uM) — INVOKE(f, v1, v2, . vN, RTy) , где cv — CV-квалификаторы g , а значения и типы привязанных аргументов v1, v2, . vN определяются следующим образом. Его можно использовать для привязки к вызываемому объекту, чтобы создать вызываемый объект с адаптированным списком аргументов и заданным типом возвращаемого значения.
Значения привязанных аргументов v1, v2, . vN и их соответствующие типы V1, V2, . VN зависят от типа соответствующего аргумента ti типа Ti в вызове функции bind и CV-квалификаторах cv оболочки вызова g следующим образом:
Если ti аргумент имеет ti.get() тип reference_wrapper vi , а его тип Vi — T& ;
Если значение std::is_bind_expression::value аргумента true vi имеет значение ti(u1, u2, . uM) , а его тип Vi — result_of::type ;
Если значение j не равно нулю аргумента std::is_placeholder::value uj vi , а его тип Vi — ; Uj&
В противном случае аргумент vi имеет ti тип & Vi Ti cv .
Например, в случае функции f(int, int) выражение bind(f, _1, 0) возвращает пересылающую оболочку вызова cw , чтобы cw(x) вызывал f(x, 0) . Выражение bind(f, 0, _1) возвращает пересылающую оболочку вызова cw , чтобы cw(x) вызывал f(0, x) .
Число аргументов в вызове bind и аргументе fn должно быть равно числу аргументов, которые могут передаваться вызываемому объекту fn . Например, правильно, bind(cos, 1.0) и bind(cos) оба и bind(cos, _1, 0.0) являются неверными.
Число аргументов в вызове функции, отправляемом в оболочку вызова, возвращаемом методом bind , должен быть не меньше наивысшего нумерованного значения is_placeholder::value для всех аргументов-местозаполнителей в вызове в bind . Например, bind(cos, _2)(0.0, 1.0) правильно (и возвращается cos(1.0) ) и bind(cos, _2)(0.0) неправильно.
Пример
// std__functional__bind.cpp // compile with: /EHsc #include #include #include using namespace std::placeholders; void square(double x) < std::cout void product(double x, double y) < std::cout int main() < double arg[] = < 1, 2, 3 >; std::for_each(&arg[0], arg + 3, square); std::cout1^2 == 1 2^2 == 4 3^2 == 9 1*2 == 2 2*2 == 4 3*2 == 6 1^2 == 1 2^2 == 4 3^2 == 9bind1st
Вспомогающая функция шаблона, которая создает адаптер для преобразования двоичного объекта функции в унарный объект функции. Он привязывает первый аргумент двоичной функции к указанному значению. Устарело в C++11, удалено в C++17.
template binder1st bind1st (const Operation& func, const Type& left);Параметры
func
Объект бинарной функции, который необходимо преобразовать в объект унарной функции.left
Значение, к которому необходимо привязать первый аргумент объекта бинарной функции.Возвращаемое значение
Унарный объект функции, который приводит к привязке первого аргумента двоичного объекта функции к значению left .
Замечания
Привязки функций — это своего рода адаптер функции. Так как они возвращают объекты функций, их можно использовать в определенных типах композиции функций для создания более сложных и мощных выражений.
Если func объект типа Operation и c является константой, то bind1st( func, c ) он совпадает с binder1st конструктором binder1st(func, c) классов и удобнее использовать его.
Пример
// functional_bind1st.cpp // compile with: /EHsc #include #include #include #include using namespace std; // Creation of a user-defined function object // that inherits from the unary_function base class class greaterthan5: unary_function < public: result_type operator()(argument_type i) < return (result_type)(i >5); > >; int main() < vectorv1; vector::iterator Iter; int i; for (i = 0; i cout 10 in the vector vector::iterator::difference_type result1a; result1a = count_if(v1.begin(), v1.end(), bind1st(less(), 10)); cout 5 in the vector // with a user defined function object vector::iterator::difference_type result1b; result1b = count_if(v1.begin(), v1.end(), greaterthan5()); cout << "The number of elements in v1 greater than 5 is: " << result1b << "." << endl; // Count the number of integers < 10 in the vector vector::iterator::difference_type result2; result2 = count_if(v1.begin(), v1.end(), bind2nd(less(), 10)); coutThe vector v1 = ( 0 5 10 15 20 25 ) The number of elements in v1 greater than 10 is: 3. The number of elements in v1 greater than 5 is: 4. The number of elements in v1 less than 10 is: 2.bind2nd
Вспомогающая функция шаблона, которая создает адаптер для преобразования двоичного объекта функции в унарный объект функции. Он привязывает второй аргумент двоичной функции к указанному значению. Устарело в C++11, удалено в C++17.
template binder2nd bind2nd(const Operation& func, const Type& right);Параметры
func
Объект бинарной функции, который необходимо преобразовать в объект унарной функции.right
Значение, к которому необходимо привязать второй аргумент объекта бинарной функции.Возвращаемое значение
Результат унарной функции при привязке второго аргумента двоичного объекта функции к right .
Замечания
Привязки функций — это своего рода адаптер функции. Так как они возвращают объекты функций, их можно использовать в определенных типах композиции функций для создания более сложных и мощных выражений.
Если func объект типа Operation и является константой, то bind2nd(func, c) он совпадает с c binder2nd конструктором binder2nd(func, c) классов и удобнее использовать его.
Пример
// functional_bind2nd.cpp // compile with: /EHsc #include #include #include #include using namespace std; // Creation of a user-defined function object // that inherits from the unary_function base class class greaterthan15: unary_function < public: result_type operator()(argument_type i) < return (result_type)(i >15); > >; int main() < vectorv1; vector::iterator Iter; int i; for (i = 0; i cout 10 in the vector vector::iterator::difference_type result1a; result1a = count_if(v1.begin(), v1.end(), bind2nd(greater(), 10)); cout 15 in the vector // with a user-defined function object vector::iterator::difference_type result1b; result1b = count_if(v1.begin(), v1.end(), greaterthan15()); cout << "The number of elements in v1 greater than 15 is: " << result1b << "." << endl; // Count the number of integers < 10 in the vector vector::iterator::difference_type result2; result2 = count_if(v1.begin(), v1.end(), bind1st(greater(), 10)); coutThe vector v1 = ( 0 5 10 15 20 25 ) The number of elements in v1 greater than 10 is: 3. The number of elements in v1 greater than 15 is: 2. The number of elements in v1 less than 10 is: 2.bit_and
Предопределенный объект функции, который выполняет побитовую операцию AND (двоичный operator& ) для его аргументов.
template struct bit_and : public binary_function; // specialized transparent functor for operator& template <> struct bit_and < template auto operator()(T&& Left, U&& Right) const -> decltype(std::forward(Left) & std::forward(Right)); >;Параметры
Type , T , U
Любой тип, поддерживающий operator& , принимающий операнды указанного или выводимого типа.Left
Левый операнд побитовой операции И. Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа T .Right
Правый операнд побитовой операции И. Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа U .Возвращаемое значение
Результат Left & Right . Специализированный шаблон выполняет точную пересылку результата типа, возвращаемого operator& .
Замечания
Функтор bit_and ограничен целочисленными типами для основных типов данных или определяемыми пользователем типами, которые реализуют бинарную operator& .
bit_not
Предопределенный объект функции, который выполняет побитовую операцию (NOT) (unary) для его аргумента operator~ . Добавлено в C++14.
template struct bit_not : public unary_function < Type operator()(const Type& Right) const; >; // specialized transparent functor for operator~ template <> struct bit_not < template auto operator()(Type&& Right) const -> decltype(~std::forward(Right)); >;Параметры
Type
Тип, поддерживающий унарный operator~ .Right
Операнд побитовой операции дополнения. Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку значения lvalue или ссылочного аргумента rvalue выводимого типа Type .Возвращаемое значение
Результат ~ Right . Специализированный шаблон выполняет точную пересылку результата типа, возвращаемого operator~ .
Замечания
Функтор bit_not ограничен целочисленными типами для основных типов данных или определяемыми пользователем типами, которые реализуют бинарную operator~ .
bit_or
Предопределенный объект функции, который выполняет побитовую операцию OR ( operator| ) в своих аргументах.
template struct bit_or : public binary_function < Type operator()( const Type& Left, const Type& Right) const; >; // specialized transparent functor for operator| template <> struct bit_or < template auto operator()(T&& Left, U&& Right) const -> decltype(std::forward(Left) | std::forward(Right)); >;Параметры
Type , T , U
Любой тип, поддерживающий operator| , принимающий операнды указанного или выводимого типа.Left
Левый операнд побитовой операции ИЛИ. Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа T .Right
Правый операнд побитовой операции ИЛИ. Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа U .Возвращаемое значение
Результат Left | Right . Специализированный шаблон выполняет точную пересылку результата типа, возвращаемого operator| .
Замечания
Функтор bit_or ограничен целочисленными типами для основных типов данных или определяемыми пользователем типами, которые реализуют operator| .
bit_xor
Предопределенный объект функции, который выполняет побитовую операцию XOR (двоичную operator^ ) по его аргументам.
template struct bit_xor : public binary_function < Type operator()( const Type& Left, const Type& Right) const; >; // specialized transparent functor for operator^ template <> struct bit_xor < template auto operator()(T&& Left, U&& Right) const -> decltype(std::forward(Left) ^ std::forward(Right)); >;Параметры
Type , T , U
Любой тип, поддерживающий operator^ , принимающий операнды указанного или выводимого типа.Left
Левый операнд побитовой операции "исключающее ИЛИ". Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа T .Right
Правый операнд побитовой операции "исключающее ИЛИ". Неспециализированный шаблон принимает ссылочный аргумент lvalue типа Type . Специализированный шаблон выполняет точную пересылку ссылочных аргументов lvalue и rvalue выводимого типа U .Возвращаемое значение
Результат Left ^ Right . Специализированный шаблон выполняет точную пересылку результата типа, возвращаемого operator^ .
Замечания
Функтор bit_xor ограничен целочисленными типами для основных типов данных или определяемыми пользователем типами, которые реализуют бинарную operator^ .
cref
Создает конструкцию reference_wrapper из аргумента.
template reference_wrapper cref(const Ty& arg); template reference_wrapper cref(const reference_wrapper& arg);Параметры
Ty
Тип аргумента, для которого создается оболочка.arg
Аргумент для создания оболочки.Замечания
Первая функция возвращает reference_wrapper(arg.get()) . Используется для создания оболочки для константной ссылки. Вторая функция возвращает reference_wrapper(arg) . Используется для восстановления константной ссылки из ссылки в оболочке.
Пример
// std__functional__cref.cpp // compile with: /EHsc #include #include int neg(int val) < return (-val); >int main()i = 1 cref(i) = 1 cref(neg)(i) = -1invoke
Вызывает любой вызываемый объект с заданными аргументами. Добавлено в C++17.
template invoke_result_t invoke(Callable&& fn, Args&&. args) noexcept(/* specification */);Параметры
Callable
Тип объекта для вызова.Args
Типы аргументов вызова.fn
Объект для вызова.args
Аргументы вызова.specification
Спецификация noexcept std::is_nothrow_invocable_v) .Замечания
Вызывает вызываемый объект fn с помощью параметров args . Фактически, INVOKE(std::forward(fn), std::forward(args). ) где псевдофукционная функция INVOKE(f, t1, t2, . tN) означает одно из следующих вещей:
- (t1.*f)(t2, . tN) , если f — это указатель на функцию-член класса T , а t1 — это объект типа T , ссылка на объект типа T или ссылка на объект типа, производного от T . То есть, когда std::is_base_of>::value это верно.
- (t1.get().*f)(t2, . tN) когда f является указателем на функцию-член класса T и std::decay_t является специализацией std::reference_wrapper .
- ((*t1).*f)(t2, . tN) если f указатель на функцию-член класса T и t1 не является одним из предыдущих типов.
- t1.*f , если N == 1, а f — это указатель на данные-член класса T и t1 — это объект типа T , ссылка на объект типа T или ссылка на объект типа, производного от T . То есть, когда std::is_base_of>::value это верно.
- t1.get().*f если N == 1 и f является указателем на данные-члены класса T и std::decay_t является специализацией std::reference_wrapper .
- (*t1).*f если N == 1 и f является указателем на данные-члены класса T и t1 не является одним из предыдущих типов.
- В остальных случаях — f(t1, t2, . tN) .
Сведения о типе результата вызываемого объекта см. в invoke_result . Предикаты для вызываемых типов см. в разделе is_invocable, is_invocable_r, is_nothrow_invocable, is_nothrow_invocable_r классов.
Пример
// functional_invoke.cpp // compile using: cl /EHsc /std:c++17 functional_invoke.cpp #include #include struct Demo < int n_; Demo(int const n) : n_<> void operator()( int const i, int const j ) const < std::cout void difference( int const i ) const < std::cout >; void divisible_by_3(int const i) < std::cout int main() < Demo d< 42 >; Demo * pd< &d >; auto pmf = &Demo::difference; auto pmd = &Demo::n_; // Invoke a function object, like calling d( 3, -7 ) std::invoke( d, 3, -7 ); // Invoke a member function, like calling // d.difference( 29 ) or (d.*pmf)( 29 ) std::invoke( &Demo::difference, d, 29 ); std::invoke( pmf, pd, 13 ); // Invoke a data member, like access to d.n_ or d.*pmd std::cout n_: " ; std::invoke( divisible_by_7, 42 ); >
Demo operator( 3, -7 ) is -21 Demo.difference( 29 ) is 13 Demo.difference( 13 ) is 29 d.n_: 42 pd->n_: 42 42 is divisible by 3. 42 is divisible by 7.
mem_fn
Создает простую оболочку вызова.
template unspecified mem_fn(RTy Ty::*pm);
Параметры
RTy
Тип возвращаемого значения функции в оболочке.
Ty
Тип указателя функции-члена.
Замечания
Функция шаблона возвращает простую оболочку cw вызова с слабым типом результата, таким образом, что выражение cw(t, a2, . aN) совпадает INVOKE(pm, t, a2, . aN) с . Он не создает никаких исключений.
Возвращаемый оболочка вызова является производным от std::unary_function (и определяет вложенный тип как синоним RTy и вложенный тип result_type argument_type в качестве синонима) cv Ty* только если тип Ty является указателем на функцию-член с квалификатором cv-qualifier cv , который не принимает аргументы.
Возвращаемый оболочка вызова является производным от std::binary_function (и определяет вложенный тип result_type как синоним RTy , вложенный тип в качестве синонима для , и вложенный тип second argument_type в качестве синонима T2 ) cv Ty* только если тип first argument_type Ty является указателем на функцию-член с квалификатором cv-qualifier cv , который принимает один аргумент типа T2 .
Пример
// std__functional__mem_fn.cpp // compile with: /EHsc #include #include class Funs < public: void square(double x) < std::cout void product(double x, double y) < std::cout >; int main()
3^2 == 9 3*2 == 6
mem_fun
Вспомогательные функции шаблона, которые используются для создания адаптеров объекта-функции для функций-членов при инициализации с аргументами указателя. Не рекомендуется использовать c++11 для mem_fn и bind удалены в C++17.
template mem_fun_t mem_fun (Result(Type::* pMem)()); template mem_fun1_t mem_fun(Result (Type::* pMem)(Arg)); template const_mem_fun_t mem_fun(Result (Type::* pMem)() const); template const_mem_fun1_t mem_fun(Result (Type::* pMem)(Arg) const);
Параметры
pMem
Указатель на функцию-член класса Type для преобразования в объект функции.
Возвращаемое значение
const Объект функции, отличный от const типа mem_fun_t или mem_fun1_t .
Пример
// functional_mem_fun.cpp // compile with: /EHsc #include #include #include #include using namespace std; class StoreVals < int val; public: StoreVals() < val = 0; >StoreVals(int j) < val = j; >bool display() < cout int squareval() < val *= val; return val; >int lessconst(int k) >; int main( ) < vectorv1; StoreVals sv1(5); v1.push_back(&sv1); StoreVals sv2(10); v1.push_back(&sv2); StoreVals sv3(15); v1.push_back(&sv3); StoreVals sv4(20); v1.push_back(&sv4); StoreVals sv5(25); v1.push_back(&sv5); cout (&StoreVals::display)); cout (&StoreVals::squareval)); cout (&StoreVals::display)); cout (&StoreVals::lessconst), 5)); cout (&StoreVals::display)); cout
mem_fun_ref
Вспомогательные функции шаблона, которые используются для создания адаптеров объекта-функции для функций-членов при инициализации с помощью ссылочных аргументов. Устарело в C++11, удалено в C++17.
template mem_fun_ref_t mem_fun_ref(Result (Type::* pMem)()); template mem_fun1_ref_t mem_fun_ref(Result (Type::* pMem)(Arg)); template const_mem_fun_ref_t mem_fun_ref(Result Type::* pMem)() const); template const_mem_fun1_ref_t mem_fun_ref(Result (T::* pMem)(Arg) const);
Параметры
pMem
Указатель на функцию-член класса Type для преобразования в объект функции.
Возвращаемое значение
Объект функции const или non_const типа mem_fun_ref_t или mem_fun1_ref_t .
Пример
// functional_mem_fun_ref.cpp // compile with: /EHsc #include #include #include #include using namespace std; class NumVals < int val; public: NumVals ( ) < val = 0; >NumVals ( int j ) < val = j; >bool display ( ) < cout bool isEven ( ) < return ( bool ) !( val %2 ); >bool isPrime( ) < if (val < 2) < return true; >for (int i = 2; i > return true; > >; int main( ) < vector v1 ( 13 ), v2 ( 13 ); vector ::iterator v1_Iter, v2_Iter; int i, k; for ( i = 0; i < 13; i++ ) v1 [ i ] = NumVals ( i+1 ); for ( k = 0; k < 13; k++ ) v2 [ k ] = NumVals ( k+1 ); coutThe original values stored in v1 are: 1 2 3 4 5 6 7 8 9 10 11 12 13 With the primes removed, the remaining values in v1 are: 4 6 8 9 10 12 The original values stored in v2 are: 1 2 3 4 5 6 7 8 9 10 11 12 13 With the even numbers removed, the remaining values are: 1 3 5 7 9 11 13not1
Возвращает дополнение унарного предиката. Не рекомендуется использовать not_fn в C++17.
template unary_negate not1(const UnaryPredicate& predicate);Параметры
predicate
Унарный предикат, знак которого должен быть изменен.Возвращаемое значение
Унарный предикат, который является отрицанием измененного унарного предиката.
Замечания
Если объект unary_negate построен из унарного предиката predicate(x) , он возвращается !predicate(x) .
Пример
// functional_not1.cpp // compile with: /EHsc #include #include #include #include using namespace std; int main() < vectorv1; vector::iterator Iter; int i; for (i = 0; i cout << "The vector v1 = ( "; for (Iter = v1.begin(); Iter != v1.end(); Iter++) cout << *Iter << " "; cout << ")" << endl; vector::iterator::difference_type result1; // Count the elements greater than 10 result1 = count_if(v1.begin(), v1.end(), bind2nd(greater(), 10)); cout << "The number of elements in v1 greater than 10 is: " << result1 << "." << endl; vector::iterator::difference_type result2; // Use the negator to count the elements less than or equal to 10 result2 = count_if(v1.begin(), v1.end(), not1(bind2nd(greater(), 10))); coutThe vector v1 = ( 0 5 10 15 20 25 30 35 ) The number of elements in v1 greater than 10 is: 5. The number of elements in v1 not greater than 10 is: 3.not2
Возвращает дополнение бинарного предиката. Не рекомендуется использовать not_fn в C++17.
template binary_negate not2(const BinaryPredicate& func);Параметры
func
Бинарный предикат, знак которого должен быть изменен.Возвращаемое значение
Бинарный предикат, который является отрицанием измененного бинарного предиката.
Замечания
Если объект binary_negate построен из двоичного предиката binary_predicate(x, y) , он возвращается !binary_predicate(x, y) .
Пример
// functional_not2.cpp // compile with: /EHsc #include #include #include #include #include int main( ) < using namespace std; vector v1; vector ::iterator Iter1; int i; v1.push_back( 6262 ); v1.push_back( 6262 ); for ( i = 0 ; i < 5 ; i++ ) < v1.push_back( rand( ) ); >cout << "Original vector v1 = ( " ; for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ ) cout << *Iter1 << " "; cout << ")" << endl; // To sort in ascending order, // use default binary predicate less( ) sort( v1.begin( ), v1.end( ) ); cout << "Sorted vector v1 = ( " ; for ( Iter1 = v1.begin( ) ; Iter1 != v1.end( ) ; Iter1++ ) cout << *Iter1 << " "; cout << ")" << endl; // To sort in descending order, // use the binary_negate helper function not2 sort( v1.begin( ), v1.end( ), not2(less( ) ) ); coutOriginal vector v1 = ( 6262 6262 41 18467 6334 26500 19169 ) Sorted vector v1 = ( 41 6262 6262 6334 18467 19169 26500 ) Resorted vector v1 = ( 26500 19169 18467 6334 6262 6262 41 )not_fn
Шаблон not_fn функции принимает вызываемый объект и возвращает вызываемый объект. Когда возвращаемый вызываемый объект позже вызывается с некоторыми аргументами, он передает их исходному вызываемому объекту и логически отрицает результат. Он сохраняет поведение категории констант квалификации и значения вызываемого объекта. not_fn является новым в C++17 и заменяет устаревшие std::not1 , и std::not2 std::unary_negate , и std::binary_negate .
template /* unspecified */ not_fn(Callable&& func);Параметры
func
Вызываемый объект, используемый для создания оболочки переадресации вызовов.Замечания
Функция шаблона возвращает оболочку вызова, например return call_wrapper(std::forward(func)) , на основе этого класса только для экспозиции:
class call_wrapper < using FD = decay_t; explicit call_wrapper(Callable&& func); public: call_wrapper(call_wrapper&&) = default; call_wrapper(call_wrapper const&) = default; template auto operator()(Args&&. ) & -> decltype(!declval>()); template auto operator()(Args&&. ) const& -> decltype(!declval>()); template auto operator()(Args&&. ) && -> decltype(!declval >()); template auto operator()(Args&&. ) const&& -> decltype(!declval>()); private: FD fd; >; Явный конструктор вызываемого объекта func требует типа std::decay_t для удовлетворения требований MoveConstructible и is_constructible_v должен иметь значение true. Он инициализирует вызываемый объект fd в оболочке и std::forward(func) создает исключение, возникающее при построении fd .
Оболочка предоставляет операторы вызовов, отличающиеся от ссылочной категории lvalue или rvalue, и квалификации const, как показано ниже:
template auto operator()(Args&&. args) & -> decltype(!declval>()); template auto operator()(Args&&. args) const& -> decltype(!declval>()); template auto operator()(Args&&. args) && -> decltype(!declval >()); template auto operator()(Args&&. args) const&& -> decltype(!declval>()); Первые два совпадают return !std::invoke(fd, std::forward(args). ) . Второе два совпадают return !std::invoke(std::move(fd), std::forward(args). ) .
Пример
// functional_not_fn_.cpp // compile with: /EHsc /std:c++17 #include #include #include #include int main() < std::vectorv1 = < 99, 6264, 41, 18467, 6334, 26500, 19169 >; auto divisible_by_3 = [](int i)< return i % 3 == 0; >; std::cout std::coutVector v1 = ( 99 6264 41 18467 6334 26500 19169 ) Elements divisible by three: 2 Elements not divisible by three: 5ptr_fun
Вспомогательные функции шаблона, которые используются для преобразования указателей на унарные и бинарные функции соответственно в унарные и бинарные адаптируемые функции. Устарело в C++11, удалено в C++17.
template pointer_to_unary_function ptr_fun(Result (*pfunc)(Arg)); template pointer_to_binary_function ptr_fun(Result (*pfunc)(Arg1, Arg2));Параметры
pfunc
Указатель на унарную или бинарную функцию, который должен быть преобразован в адаптируемую функцию.Возвращаемое значение
Первая функция шаблона возвращает унарную функцию pointer_to_unary_function< Arg ( Result >(* ). pfunc
Вторая функция шаблона возвращает двоичные функции pointer_to_binary_function Arg1 , Arg2 ( Result >* ). pfunc
Замечания
Указатель функции — это объект функции. Он может быть передан любому алгоритму, который ожидает функцию в качестве параметра, но она не адаптируется. Сведения о вложенных типах требуются для его использования с адаптером, например для привязки значения к нему или для его отмены. Преобразование указателей на унарные и бинарные функции с помощью вспомогательной функции ptr_fun позволяет адаптерам функций работать с такими указателями.
Пример
// functional_ptr_fun.cpp // compile with: /EHsc #include #include #include #include #include int main( ) < using namespace std; vector v1; vector ::iterator Iter1, RIter; v1.push_back ( "Open" ); v1.push_back ( "up" ); v1.push_back ( "the" ); v1.push_back ( "opalescent" ); v1.push_back ( "gates" ); cout >ref
Создает reference_wrapper из аргумента.
template reference_wrapper ref(Ty& arg); template reference_wrapper ref(reference_wrapper& arg);Возвращаемое значение
Ссылка на arg , а именно reference_wrapper(arg) .
Пример
В приведенном ниже примере определяются две функции: одна из них привязана к строковой переменной, а другая — к ссылке строковой переменной, вычисленной с помощью вызова ref . Когда значение переменной меняется, первая функция продолжает использовать старое значение, а вторая использует новое значение.
#include #include #include #include #include #include #include using namespace std; using namespace std; using namespace std::placeholders; bool shorter_than(const string& l, const string& r) < return l.size() < r.size(); >int main() < vectorv_original; v_original.push_back("tiger"); v_original.push_back("cat"); v_original.push_back("lion"); v_original.push_back("cougar"); copy(v_original.begin(), v_original.end(), ostream_iterator(cout, " ")); cout f = bind(shorter_than, _1, s); function f_ref = bind(shorter_than, _1, ref(s)); vector v; // Remove elements that are shorter than s ("meow") v = v_original; v.erase(remove_if(v.begin(), v.end(), f), v.end()); copy(v.begin(), v.end(), ostream_iterator(cout, " ")); cout << endl; // Now change the value of s. // f_ref, which is bound to ref(s), will use the // new value, while f is still bound to the old value. s = "kitty"; // Remove elements that are shorter than "meow" (f is bound to old value of s) v = v_original; v.erase(remove_if(v.begin(), v.end(), f), v.end()); copy(v.begin(), v.end(), ostream_iterator(cout, " ")); cout << endl; // Remove elements that are shorter than "kitty" (f_ref is bound to ref(s)) v = v_original; v.erase(remove_if(v.begin(), v.end(), f_ref), v.end()); copy(v.begin(), v.end(), ostream_iterator(cout, " ")); couttiger cat lion cougar tiger lion cougar tiger lion cougar tiger cougarswap
Меняет местами два объекта function .
template void swap(function& f1, function& f2);Параметры
FT
Тип, управляемый объектами функции.f1
Первый объект функции.f2
Второй объект функции.Замечания
Функция возвращает f1.swap(f2) .
Пример
// std__functional__swap.cpp // compile with: /EHsc #include #include int neg(int val) < return (-val); >int main() < std::functionfn0(neg); std::cout << std::boolalpha << "empty == " << !fn0 << std::endl; std::cout << "val == " << fn0(3) << std::endl; std::functionfn1; std::coutempty == false val == -3 empty == true empty == true empty == false val == -3Функции
Теги: Функции в си, прототип, описание, определение, вызов. Формальные параметры и фактические параметры. Аргументы функции, передача по значению, передача по указателю. Возврат значения.
Введение
Ч ем дальше мы изучаем си, тем больше становятся программы. Мы собираем все действия в одну функцию main и по несколько раз копируем одни и те же действия, создаём десятки переменных с уникальными именами. Наши программы распухают и становятся всё менее и менее понятными, ветвления становятся всё длиннее и ветвистее.
Но из сложившейся ситуации есть выход! Теперь мы научимся создавать функции на си. Функции, во-первых, помогут выделить в отдельные подпрограммы дублирующийся код, во-вторых, помогут логически разбить программу на части, в-третьих, с функциями в си связано много особенностей, которые позволят использовать новые подходы к структурированию приложений.
Функция – это именованная часть программы, которая может быть многократно вызвана из другого участка программы (в котором эта функция видна). Функция может принимать фиксированное либо переменное число аргументов, а может не иметь аргументов. Функция может как возвращать значение, так и быть пустой (void) и ничего не возвращать.
Мы уже знакомы с многими функциями и знаем, как их вызывать – это функции библиотек stdio, stdlib, string, conio и пр. Более того, main – это тоже функция. Она отличается от остальных только тем, что является точкой входа при запуске приложения.
Функция в си определяется в глобальном контексте. Синтаксис функции:Самый простой пример – функция, которая принимает число типа float и возвращает квадрат этого числа
#include #include float sqr(float x) < float tmp = x*x; return tmp; >void main()
Внутри функции sqr мы создали локальную переменную, которой присвоили значение аргумента. В качестве аргумента функции передали число 9,3. Служебное слово return возвращает значение переменной tmp. Можно переписать функцию следующим образом:
float sqr(float x)
В данном случае сначала будет выполнено умножение, а после этого возврат значения. В том случае, если функция ничего не возвращает, типом возвращаемого значения будет void. Например, функция, которая печатает квадрат числа:
void printSqr(float x)
в данном случа return означает выход из функции. Если функция ничего не возвращает, то return можно не писать. Тогда функция доработает до конца и произойдёт возврат управления вызывающей функции.
void printSqr(float x)
Если функция не принимает аргументов, то скобки оставляют пустыми. Можно также написать слово void:
void printHelloWorld()
void printHelloWorld(void)Формальные и фактические параметры
П ри объявлении функции указываются формальные параметры, которые потом используются внутри самой функции. При вызове функции мы используем фактические параметры. Фактическими параметрами могут быть переменные любого подходящего типа или константы.
Например, пусть есть функция, которая возвращает квадрат числа и функция, которая суммирует два числа.
#include #include //Формальные параметры имеют имена a и b //по ним мы обращаемся к переданным аргументам внутри функции int sum(int a, int b) < return a+b; >float square(float x) < return x*x; >void main() < //Фактические параметры могут иметь любое имя, в том числе и не иметь имени int one = 1; float two = 2.0; //Передаём переменные, вторая переменная приводится к нужному типу printf("%d\n", sum(one, two)); //Передаём числовые константы printf("%d\n", sum(10, 20)); //Передаём числовые константы неверного типа, они автоматически приводится к нужному printf("%d\n", sum(10, 20.f)); //Переменная целого типа приводится к типу с плавающей точкой printf("%.3f\n", square(one)); //В качестве аргумента может выступать и вызов функции, которая возвращает нужное значение printf("%.3f\n", square(sum(2 + 4, 3))); getch(); >Обращаю внимание, что приведение типов просиходит неявно и только тогда, когда это возможно. Если функция получает число в качестве аргумента, то нельзя ей передать переменную строку, например "20" и т.д. Вообще, лучше всегда использовать верный тип или явно приводить тип к нужному.
Если функция возвращает значение, то оно не обязательно должно быть сохранено. Например, мы пользуемся функцией getch, которая считывает символ и возвращает его.#include #include void main() < char c; do < //Сохраняем возвращённое значение в переменную c = getch(); printf("%c", c); >while(c != 'q'); //Возвращённое значение не сохраняется getch(); >Передача аргументов
При передаче аргументов происходит их копирование. Это значит, что любые изменения, которые функция производит над переменными, имеют место быть только внутри функции. Например
#include #include void change(int a) < a = 100; printf("%d\n", a); >void main()
Программы выведет
200
100
200
Понятно почему. Внутри функции мы работаем с переменной x, которая является копией переменной d. Мы изменяем локальную копию, но сама переменная d при этом не меняется. После выхода из функции локальная переменная будет уничтожена. Переменная d при этом никак не изменится.
Каким образом тогда можно изменить переменную? Для этого нужно передать адрес этой переменной. Перепишем функцию, чтобы она принимала указатель типа int#include #include void change(int *a) < *a = 100; printf("%d\n", *a); >void main()
Вот теперь программа выводит
200
100
100
Здесь также была создана локальная переменная, но так как передан был адрес, то мы изменили значение переменной d, используя её адрес в оперативной памяти.В программировании первый способ передачи параметров называют передачей по значению, второй – передачей по указателю. Запомните простое правило: если вы хотите изменить переменную, необходимо передавать функции указатель на эту переменную. Следовательно, чтобы изменить указатель, необходимо передавать указатель на указатель и т.д. Например, напишем функцию, которая будет принимать размер массива типа int и создавать его. С первого взгляда, функция должна выглядеть как-то так:
#include #include #include void init(int *a, unsigned size) < a = (int*) malloc(size * sizeof(int)); >void main() < int *a = NULL; init(a, 100); if (a == NULL) < printf("ERROR"); >else < printf("OKAY. "); free(a); >getch(); >Но эта функция выведет ERROR. Мы передали адрес переменной. Внутри функции init была создана локальная переменная a, которая хранит адрес массива. После выхода из функции эта локальная переменная была уничтожена. Кроме того, что мы не смогли добиться нужного результата, у нас обнаружилась утечка памяти: была выделена память на куче, но уже не существует переменной, которая бы хранила адрес этого участка.
Для изменения объекта необходимо передавать указатель на него, в данном случае – указатель на указатель.
#include #include #include void init(int **a, unsigned size) < *a = (int*) malloc(size * sizeof(int)); >void main() < int *a = NULL; init(&a, 100); if (a == NULL) < printf("ERROR"); >else < printf("OKAY. "); free(a); >getch(); >Вот теперь всё работает как надо.
Ещё подобный пример. Напишем функцию, которая принимает в качестве аргумента строку и возвращает указатель на область памяти, в которую скопирована эта строка.#include #include #include #include char* initByString(const char *str) < char *p = (char*) malloc(strlen(str) + 1); strcpy(p, str); return p; >void main()
В этом примере утечки памяти не происходит. Мы выделили память с помощью функции malloc, скопировали туда строку, а после этого вернули указатель. Локальные переменные были удалены, но переменная test хранит адрес участка памяти на куче, поэтому можно его удалить с помощью функции free.
Объявление функции и определение функции. Создание собственной библиотеки
В си можно объявить функцию до её определения. Объявление функции, её прототип, состоит из возвращаемого значения, имени функции и типа аргументов. Имена аргументов можно не писать. Например
#include #include //Прототипы функций. Имена аргументов можно не писать int odd(int); int even(int); void main() < printf("if %d odd? %d\n", 11, odd(11)); printf("if %d odd? %d\n", 10, odd(10)); getch(); >//Определение функций int even(int a) < if (a) < odd(--a); >else < return 1; >> int odd(int a) < if (a) < even(--a); >else < return 0; >>Это смешанная рекурсия – функция odd возвращает 1, если число нечётное и 0, если чётное.
Обычно объявление функции помещают отдельно, в .h файл, а определение функций в .c файл. Таким образом, заголовочный файл представляет собой интерфейс библиотеки и показывает, как с ней работать, не вдаваясь в содержимое кода.
Давайте создадим простую библиотеку. Для этого нужно будет создать два файла – один с расширением .h и поместить туда прототипы функций, а другой с расширением .c и поместить туда определения этих функций. Если вы работаете с IDE, то .h файл необходимо создавать в папке Заголовочные файлы, а файлы кода в папке Файлы исходного кода. Пусть файлы называются File1.h и File1.c
Перепишем предыдущий код. Вот так будет выглядеть заголовочный файл File1.h#ifndef _FILE1_H_ #define _FILE1_H_ int odd(int); int even(int); #endifСодержимое файла исходного кода File1.c
#include "File1.h" int even(int a) < if (a) < odd(--a); >else < return 1; >> int odd(int a) < if (a) < even(--a); >else < return 0; >>Наша функция main
#include #include #include "File1.h" void main()
Рассмотрим особенности каждого файла. Наш файл, который содержит функцию main, подключает необходимые ему библиотеки а также заголовочный файл File1.h. Теперь компилятору известны прототипы функций, то есть он знает возвращаемый тип, количество и тип аргументов и имена функций.
Заголовочный файл, как и оговаривалось ранее, содержит прототип функций. Также здесь могут быть подключены используемые библиотеки. Макрозащита #define _FILE1_H_ и т.д. используется для предотвращения повторного копирования кода библиотеки при компиляции. Эти строчки можно заменить одной
#pragma once int odd(int); int even(int);Файл File1.c исходного кода подключает свой заголовочный файл. Всё как обычно логично и просто. В заголовочные файлах принято кроме прототипов функций выносить константы, макроподстановки и определять новые типы данных. Кроме того, именно в заголовочных файлах можно обширно комментировать код и писать примеры его использования.
Передача массива в качестве аргумента
К ак уже говорилось ранее, имя массива подменяется на указатель, поэтому передача одномерного массива эквивалентна передаче указателя. Пример: функция получает массив и его размер и выводит на печать:
#include #include void printArray(int *arr, unsigned size) < unsigned i; for (i = 0; i < size; i++) < printf("%d ", arr[i]); >> void main() < int x[10] = ; printArray(x, 10); getch(); >В этом примере функция может иметь следующий вид
void printArray(int arr[], unsigned size) < unsigned i; for (i = 0; i < size; i++) < printf("%d ", arr[i]); >>Также напомню, что правило подмены массива на указатель не рекурсивное. Это значит, что необходимо указывать размерность двумерного массива при передаче
#include #include void printArray(int arr[][5], unsigned size) < unsigned i, j; for (i = 0; i < size; i++) < for (j = 0; j < 5; j++) < printf("%d ", arr[i][j]); >printf("\n"); > > void main() < int x[][5] = < < 1, 2, 3, 4, 5>, < 6, 7, 8, 9, 10>>; printArray(x, 2); getch(); >Либо, можно писать
#include #include void printArray(int (*arr)[5], unsigned size) < unsigned i, j; for (i = 0; i < size; i++) < for (j = 0; j < 5; j++) < printf("%d ", arr[i][j]); >printf("\n"); > > void main() < int x[][5] = < < 1, 2, 3, 4, 5>, < 6, 7, 8, 9, 10>>; printArray(x, 2); getch(); >Если двумерный массив создан динамически, то можно передавать указатель на указатель. Например функция, которая получает массив слов и возвращает массив целых, равных длине каждого слова:
#include #include #include #include #define SIZE 10 unsigned* getLengths(const char **words, unsigned size) < unsigned *lengths = NULL; unsigned i; lengths = (unsigned*) malloc(size * sizeof(unsigned)); for (i = 0; i < size; i++) < lengths[i] = strlen(words[i]); >return lengths; > void main() < char **words = NULL; char buffer[128]; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) < printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); >len = getLengths(words, SIZE); for (i = 0; i < SIZE; i++) < printf("%d ", len[i]); free(words[i]); >free(words); free(len); getch(); >Можно вместо того, чтобы возвращать указатель на массив, передавать массив, который необходимо заполнить
#include #include #include #include #define SIZE 10 void getLengths(const char **words, unsigned size, unsigned *out) < unsigned i; for (i = 0; i < size; i++) < out[i] = strlen(words[i]); >> void main() < char **words = NULL; char buffer[128]; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) < printf("%d. ", i); scanf("%127s", buffer); words[i] = (char*) malloc(128); strcpy(words[i], buffer); >len = (unsigned*) malloc(SIZE * sizeof(unsigned)); getLengths(words, SIZE, len); for (i = 0; i < SIZE; i++) < printf("%d ", len[i]); free(words[i]); >free(words); free(len); getch(); >На этом первое знакомство с функциями заканчивается: тема очень большая и разбита на несколько статей.
ru-Cyrl 18- tutorial Sypachev S.S. 1989-04-14 sypachev_s_s@mail.ru Stepan Sypachev students
Всё ещё не понятно? – пиши вопросы на ящик
