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

C как сделать описание функции

  • автор:

Функции

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

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

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

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

Функции

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

Введение

Ч ем дальше мы изучаем си, тем больше становятся программы. Мы собираем все действия в одну функцию 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

email

Всё ещё не понятно? – пиши вопросы на ящик

Функции (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++, т. к. это довольно сложная тема и мы еще будем разбирать примеры с функциями в следующих уроках.

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

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

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

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