18.10.2023

Определение и вызов функций. Функции в языке программирования C Все функции c


Последнее обновление: 22.09.2017

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

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

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

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

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

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

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

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

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

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

Int main() { return 0; }

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

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

Void hello() { std::cout << "hello\n"; }

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

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

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

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

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

#include void hello() { std::cout << "hello\n"; } int main() { hello(); hello(); return 0; }

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

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

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

#include int main() { hello(); hello(); return 0; } void hello() { std::cout << "hello\n"; }

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

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

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

Void hello();

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

#include void hello(); int main() { hello(); hello(); return 0; } void hello() { std::cout << "hello\n"; }

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

Для чего нужны функции в C?

Функции в Си применяются для выполнения определённых действий в рамках общей программы. Программист сам решает какие именно действия вывести в функции. Особенно удобно применять функции для многократно повторяющихся действий.

Простой пример функции в Cи

Пример функции в Cи:

#include #include int main(void) { puts("Functions in C"); return EXIT_SUCCESS; }

Это очень простая программа на Си. Она просто выводит строку «Functions in C». В программе имеется единственная функция под названием main. Рассмотрим эту функцию подробно. В заголовке функции, т.е. в строке

int – это тип возвращаемого функцией значения;

main - это имя функции;

(void) - это перечень аргументов функции. Слово void указывает, что у данной функции нет аргументов;

return – это оператор, который завершает выполнение функции и возвращает результат работы функции в точку вызова этой функции;

EXIT_SUCCESS - это значение, равное нулю. Оно определено в файле stdlib.h;

часть функции после заголовка, заключенная в фигурные скобки

{
puts("Functions in C");
return EXIT_SUCCESS;
}

называют телом функции.

Итак, когда мы работаем с функцией надо указать имя функции, у нас это main, тип возвращаемого функцией значения, у нас это int, дать перечень аргументов в круглых скобках после имени функции, у нас нет аргументов, поэтому пишем void, в теле функции выполнить какие-то действия (ради них и создавалась функция) и вернуть результат работы функции оператором return. Вот основное, что нужно знать про функции в C.

Как из одной функции в Cи вызвать другую функцию?

Рассмотрим пример вызова функций в Си:

/* Author: @author Subbotin B.P..h> #include int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

Запускаем на выполнение и получаем:

В этом примере создана функция sum, которая складывает два целых числа и возвращает результат. Разберём подробно устройство этой функции.

Заголовок функции sum:

int sum(int a, int b)

здесь int - это тип возвращаемого функцией значения;

sum - это имя функции;

(int a, int b) - в круглых скобках после имени функции дан перечень её аргументов: первый аргумент int a, второй аргумент int b. Имена аргументов являются формальными, т.е. при вызове функции мы не обязаны отправлять в эту функцию в качестве аргументов значения перемнных с именами a и b. В функции main мы вызываем функцию sum так: sum(d, e);. Но важно, чтоб переданные в функцию аргументы совпадали по типу с объявленными в функции.

В теле функции sum, т.е. внутри фигурных скобок после заголовка функции, мы создаем локальную переменную int c, присваиваем ей значение суммы a плюс b и возвращаем её в качестве результата работы функции опрератором return.

Теперь посмотрим как функция sum вызывается из функции main.

Вот функция main:

Int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; }

Сначала мы создаём две переменных типа int

Int d = 1; int e = 2;

их мы передадим в функцию sum в качестве значений аргументов.

int f = sum(d, e);

её значением будет результат работы функции sum, т.е. мы вызываем функцию sum, которая возвратит значение типа int, его-то мы и присваиваем переменной f. В качестве аргументов передаём d и f. Но в заголовке функции sum

int sum(int a, int b)

аргументы называются a и b, почему тогда мы передаем d и f? Потому что в заголовке функций пишут формальные аргументы, т.е. НЕ важны названия аргументов, а важны их типы. У функции sum оба аргумента имеют тип int, значит при вызове этой функции надо передать два аргумента типа int с любыми названиями.

Ещё одна тонкость. Функция должна быть объявлена до места её первого вызова. В нашем примере так и было: сначала объявлена функция sum, а уж после мы вызываем её из функции main. Если функция объявляется после места её вызова, то следует использовать прототип функции.

Прототип функции в Си

Рассмотрим пример функциив Си:

/* Author: @author Subbotin B.P..h> #include int sum(int a, int b); int main(void) { puts("Functions in C"); int d = 1; int e = 2; int f = sum(d, e); printf("1 + 2 = %d", f); return EXIT_SUCCESS; } int sum(int a, int b) { int c = 0; c = a + b; return c; }

В этом примере функция sum определена ниже места её вызова в функции main. В таком случае надо использовать прототип функции sum. Прототип у нас объявлен выше функции main:

int sum(int a, int b);

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

int f = sum(d, e);

а ниже функции main определяем функцию sum, которая предварительно была объявлена в прототипе:

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

Чем объявление функции в Си отличается от определения функции в Си?

Когда мы пишем прототип функции, например так:

int sum(int a, int b);

то мы объявляем функцию.

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

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

то мы определяем функцию.

Оператор return

Оператор return завершает работу функции в C и возвращает результат её работы в точку вызова. Пример:

Int sum(int a, int b) { int c = 0; c = a + b; return c; }

Эту функцию можно упростить:

Int sum(int a, int b) { return a + b; }

здесь оператор return вернёт значение суммы a + b.

Операторов return в одной функции может быть несколько. Пример:

Int sum(int a, int b) { if(a > 2) { return 0;// Первый случай; } if(b < 0) { return 0;// Второй случай; } return a + b; }

Если в примере значение аргумента a окажется больше двух, то функция вернет ноль (первый случай) и всё, что ниже комментария «// Первый случай;» выполнятся не будет. Если a будет меньше двух, но b будет меньше нуля, то функция завершит свою работу и всё, что ниже комментария «// Второй случай;» выполнятся не будет.

И только если оба предыдущих условия не выполняются, то выполнение программы дойдёт до последнего оператора return и будет возвращена сумма a + b.

Передача аргументов функции по значению

Аргументы можно передавать в функцию C по значению. Пример:

/* Author: @author Subbotin B.P..h> #include int sum(int a) { return a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(d)); printf("d = %d", d); return EXIT_SUCCESS; }

В примере, в функции main, создаём переменную int d = 10. Передаём по значению эту переменную в функцию sum(d). Внутри функции sum значение переменной увеличивается на 5. Но в функции main значение d не изменится, ведь она была передана по значению. Это означает, что было передано значение переменной, а не сама переменная. Об этом говорит и результат работы программы:

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

Передача указателей функции Си

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

/* Author: @author Subbotin B.P..h> #include int sum(int *a) { return *a += 5; } int main(void) { puts("Functions in C"); int d = 10; printf("sum = %d\n", sum(&d)); printf("d = %d", d); return EXIT_SUCCESS; }

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

printf("sum = %d\n", sum(&d));

в функцию sum передается не значение переменной d, равное 10-ти, а адрес этой переменной, вот так:

Теперь посмотрим на функцию sum:

Int sum(int *a) { return *a += 5; }

Аргументом её является указатель на int. Мы знаем, что указатель - это переменная, значением которой является адрес какого-то объекта. Адрес переменной d отправляем в функцию sum:

Внутри sum указатель int *a разыменовывается. Это позволяет от указателя перейти к самой переменной, на которую и указывает наш указатель. А в нашем случае это переменная d, т.е. выражение

равносильно выражению

Результат: функция sum изменяет значение переменной d:

На этот раз изменяется значение d после возврата из sum, чего не наблюдалось в предыдущм пункте, когда мы передавали аргумент по значению.

C/C++ в Eclipse

Все примеры для этой статьи я сделал в Eclipse. Как работать с C/C++ в Eclipse можно посмотреть . Если вы работаете в другой среде, то примеры и там будут работать.

Пожалуйста, приостановите работу AdBlock на этом сайте.

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

Помните, мы говорили о парадигмах программирования, а точнее о структурном программировании. Основной идеей там было то, что любую программу можно можно написать используя только три основных конструкции: следование, условие и цикл. Теперь к этим конструкциям мы добавим ещё одну – «подпрограммы» – и получим новую парадигму процедурное программирование» .

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

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

Как устроены функции

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

Листинг 1.

Int main(void){ // заголовок функции // в фигурных скобках записано тело функции }

С телом функции всё ясно: там описывается алгоритм работы функции. Давайте разберёмся с заголовком. Он состоит из трёх обязательных частей:

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

Сначала записывается тип возвращаемого значения, например, int , как в функции main . Если функция не должна возвращать никакое значение в программу, то на этом месте пишется ключевое слово void . Казалось бы, что раз функция ничего не возвращает, то и не нужно ничего писать. Раньше, кстати, в языке Си так и было сделано, но потом для единообразия всё-таки добавили. Сейчас современные компиляторы будут выдавать предупреждения/ошибки, если вы не укажете тип возвращаемого значения.
В некоторых языках программирования функции, которые не возвращают никакого значения, называют процедурами (например, pascal). Более того, для создания функций и процедур предусмотрен различный синтаксис. В языке Си такой дискриминации нет.

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

Давайте посмотрим на заголовки уже знакомых нам функций.

Листинг 2.

// функция с именем srand, принимающая целое число, ничего не возвращает void srand(int) //функция с именем sqrt, принимающая вещественное число типа float, возвращает вещественное число типа float float sqrt(float) //функция с именем rand, которая не принимает аргументов, возвращает целое число int rand(void) //функция с именем pow, принимающая два аргумента типа double, возвращает вещественное число типа double double pow(double, double)

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

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

Рис.1 Уточнение структуры программы. Объявление функций.

Как видите, имеется аж два места, где это можно сделать.

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

Листинг 3.

#include // объявляем пользовательскую функцию с именем max_num // вход: два целочисленных параметра с именами a и b // выход: максимальное из двух аргументов int max_num(int a, int b){ int max = b; if (a > b) max = a; return max; } //основная программа int main(void) { int x = 0, y = 0; int m = 0; scanf("%d %d", &x, &y); m = max_num(x,y); printf("max(%d,%d) = %d\n",x,y,m); return 0; }

Давайте я подробно опишу, как будет работать эта программа. Выполняется тело функции main . Создются целые переменные x , y и m . В переменные x и y считываются данные с клавиатуры. Допустим мы ввели 3 5 , тогда x = 3 , y = 5 . Это вам всё и так должно быть понятно. Теперь следующая строчка

Листинг 4.

M = max_num(x,y);

Переменной m надо присвоить то, что находится справа от знака = . Там у нас указано имя функции, которую мы создали сами. Компьютер ищет объявление и описание этой функции. Оно находится выше. Согласно этому объявлению данная функция должна принять два целочисленных значения. В нашем случае это значения, записанные в переменных x и y . Т.е. числа 3 и 5 . Обратите внимание, что в функцию передаются не сами переменные x и y , а только значения (два числа), которые в них хранятся. То, что на самом деле передаётся в функцию при её вызове в программе, называется фактическими параметрами функции.

Теперь начинает выполняться функция max_num . Первым делом для каждого параметра, описанного в заголовке функции, создается отдельная временная переменная. В нашем случае создаются две целочисленных переменных с именами a и b . Этим переменным присваиваются значения фактических параметров. Сами же параметры, описанные в заголовке функции, называются формальными параметрами. Итак, формальным параметрам a и b присваиваются значения фактических параметров 3 и 5 соответственно. Теперь a = 3 , b = 5 . Дальше внутри функции мы можем работать с этими переменными так, как будто они обычные переменные.

Создаётся целочисленная переменная с именем max , ей присваивается значение b . Дальше проверяется условие a > b . Если оно истинно, то значение в переменной max следует заменить на a .

Далее следует оператор return , который возвращает в вызывающую программу (функцию main ) значение, записанное в переменной max , т.е. 5 . После чего переменные a , b и max удаляются из памяти. А мы возвращаемся к строке

Листинг 5.

M = max_num(x,y);

Функция max_num вернула значение 5 , значит теперь справа от знака = записано 5 . Это значение записывается в переменную m. Дальше на экран выводится строчка, и программа завершается.

Внимательно прочитайте последние 4 абазаца ещё раз, чтобы до конца уяснить, как работает программа.

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

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

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

Листинг 6.

#include int max_num(int, int); int main(void) { int x =0, y = 0; int m = 0; scanf("%d %d", &x, &y); m = max_num(x,y); printf("max(%d,%d) = %d\n",x,y,m); return 0; } int max_num(int a, int b){ int max = b; if (a > b) max = a; return max; }

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

До сих пор мы писали программы единым, функционально неделимым, кодом. Алгоритм программы находился в главной функции, причём других функций в программе не было. Мы писали маленькие программы, поэтому не было потребности в объявлении своих функций. Для написания больших программ, опыт показывает, что лучше пользоваться функциями. Программа будет состоять из отдельных фрагментов кода, под отдельным фрагментом кода понимается функция. Отдельным, потому, что работа отдельной функции не зависит от работы какой-нибудь другой. То есть алгоритм в каждой функции функционально достаточен и не зависим от других алгоритмов программы. Однажды написав функцию, её можно будет с лёгкостью переносить в другие программы. Функция (в программировании) — это фрагмент кода или алгоритм, реализованный на каком-то языке программирования, с целью выполнения определённой последовательности операций. Итак, функции позволяют сделать программу модульной, то есть разделить программу на несколько маленьких подпрограмм (функций), которые в совокупности выполняют поставленную задачу. Еще один огромнейший плюс функций в том, что их можно многократно использовать. Данная возможность позволяет многократно использовать один раз написанный код, что в свою очередь, намного сокращает объем кода программы!

Кроме того, что в С++ предусмотрено объявление своих функций, также можно воспользоваться функциями определёнными в стандартных заголовочных файлах языка программирования С++. Чтобы воспользоваться функцией, определённой в заголовочном файле, нужно его подключить. Например, чтобы воспользоваться функцией, которая возводит некоторое число в степень, нужно подключить заголовочный файл ив запустить функцию pow() в теле программы. Разработаем программу, в которой запустим функцию pow() .

// inc_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" //действие 1 - подключаем заголовочный файл

// код Code::Blocks

// код Dev-C++

// inc_func.cpp: определяет точку входа для консольного приложения. //действие 1 - подключаем заголовочный файл который содержит прототипы основных математических функций #include int main(int argc, char* argv) { float power = pow(3.14,2); //действие 2 - запуск функции возведения числа в степень return 0; }

Подключение заголовочных файлов выполняется так, как показано в строке 5 , т. е. объявляется препроцессорная директива #include , после чего внутри знаков <> пишется имя заголовочного файла. Когда подключен заголовочный файл, можно использовать функцию, что, и сделано в строке 9 .Функция pow() возводит число 3.14 в квадрат и присваивает полученный результат переменной power , где
pow — имя функции;
числа 3.14 и 2 — аргументы функции;

Всегда после имени функции ставятся круглые скобочки, внутри которых, функции передаются аргументы, и если аргументов несколько, то они отделяются друг от друга запятыми. Аргументы нужны для того, чтобы функции передать информацию. Например, чтобы возвести число 3.14 в квадрат используя функцию pow() , нужно как-то этой функции сообщить, какое число, и в какую степень его возводить. Вот именно для этого и придуманы аргументы функций, но бывают функции, в которых аргументы не передаются, такие функции вызываются с пустыми круглыми скобочками. Итак, для того, чтобы воспользоваться функцией из стандартного заголовочного файла С++ необходимо выполнить два действия:

  1. Подключить необходимый заголовочный файл;
  2. Запустить нужную функцию.

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

  1. Функции, которые не возвращают значений
  2. Функции, возвращающие значение

Функции, не возвращающие значения, завершив свою работу, никакого ответа программе не дают. Рассмотрим структуру объявления таких функций.

// структура объявления функций не возвращающих значений void /*имя функции*/(/*параметры функции*/) // заголовок функции { // тело функции }

Строка 2 начинается с зарезервированного слова void — это тип данных, который не может хранить какие-либо данные. Тип данных void говорит о том, что данная функция не возвращает никаких значений. void никак по-другому не используется и нужен только для того, чтобы компилятор мог определить тип функции. После зарезервированного слова void пишется имя функции. Сразу за именем функции ставятся две круглые скобочки, открывающаяся и закрывающаяся. Если нужно функции передавать какие-то данные, то внутри круглых скобочек объявляются параметры функции, они отделяются друг от друга запятыми. Строка 2 называется заголовком функции. После заголовка функции пишутся две фигурные скобочки, внутри которых находится алгоритм, называемый телом функции. Разработаем программу, в которой объявим функцию нахождения факториала, причём функция не должна возвращать значение.

<= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult cout << numb << "! = " << rezult << endl; // печать значения n! } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; faktorial(digit);// запуск функции нахождения факториала system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

using namespace std; // объявление функции нахождения n! void faktorial(int numb)// заголовок функции { int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult cout << numb << "! = " << rezult << endl; // печать значения n! } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; faktorial(digit);// запуск функции нахождения факториала return 0; }

После того, как были подключены все необходимые заголовочные файлы, можно объявлять функцию нахождения факториала.Под объявлением функции подразумевается выбор имени функции, определение параметров функции и написание алгоритма, который является телом функции. После выполнения этих действий функцию можно использовать в программе. Так как функция не должна возвращать значение, то тип возвращаемых данных должен быть void . Имя функции — faktorial , внутри круглых скобочек объявлена переменная numb типа int . Эта переменная является параметром функции faktorial() . Таким образом, все объявления в строке 8 в совокупности составляют заголовок функции. Строки 9 — 14 составляют тело функции faktorial() . Внутри тела в строке 10 объявлена переменная rezult , которая будет хранить результат нахождения n! После чего, в строках 11-12 Объявлен оператор цикла for для нахождения факториала. В строке 13 объявлен оператор cout , с помощью которого значение факториала будет печататься на экране. Теперь, когда функция объявлена можно воспользоваться ею. В строке 21 запускается функция faktorial(digit) , внутри скобочек функции передаётся аргумент, т. е. значение, содержащееся в переменной digit . Результат работы программы (см. Рисунок 1).

Рисунок 1 — Функции в С++

Итак, после запуска программы, было введена цифра 5, и программа вычислила, что значение 120 это 5!.

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

// структура объявления функций возвращающих значения /*возвращаемый тип данных*/ /*имя функции*/(/*параметры функции*/) // заголовок функции { // тело функции return /*возвращаемое значение*/; }

Структура объявления функций осталась почти неизменной, за исключением двух строк. В заголовке функции сначала нужно определять возвращаемый тип данных, это может быть тип данных int , если необходимо возвратить целое число или тип данных float — для чисел с плавающей точкой. В общем, любой другой тип данных, всё зависит от того, что функция должна вернуть. Так как функция должна вернуть значение, то для этого должен быть предусмотрен специальный механизм, как в строке 5 . C помощью зарезервированного слова return можно вернуть значение, по завершении работы функции. Всё, что нужно, так это указать переменную, содержащую нужное значение, или некоторое значение, после оператора return . Тип данных возвращаемого значения в строке 5 должен совпадать с типом данных в строке 2 . Переделаем программу нахождения факториала так, чтобы функция faktorial() возвращала значение факториала.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult return rezult; // передаём значение факториала в главную функцию } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; cout << digit << "! = " << faktorial(digit) << endl;// запуск функции нахождения факториала system("pause"); return 0; }

// код Code::Blocks

// код Dev-C++

// struct_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; // объявление функции нахождения n! int faktorial(int numb)// заголовок функции { int rezult = 1; // инициализируем переменную rezult значением 1 for (int i = 1; i <= numb; i++) // цикл вычисления значения n! rezult *= i; // накапливаем произведение в переменной rezult return rezult; // передаём значение факториала в главную функцию } int main(int argc, char* argv) { int digit; // переменная для хранения значения n! cout << "Enter number: "; cin >> digit; cout << digit << "! = " << faktorial(digit) << endl;// запуск функции нахождения факториала return 0; }

Теперь функция faktorial() имеет возвращаемый тип данных — int , так как n! — это целое число.В строке 13 объявлен оператор return , который возвращает значение, содержащееся в переменной rezult . В строке 21 выполняем запуск функции faktorial() ,возвращаемое значение которой отправляем в поток вывода с помощью оператора cout . Можно было бы написать так int fakt = faktorial(digit); — переменной типа int присваиваем возвращаемое значение функции faktorial() , после чего в переменной fakt будет храниться значение n! . Результат работы программы не изменился (см. Рисунок 2).

Enter number: 5 5! = 120 Для продолжения нажмите любую клавишу. . .

Рисунок 2 — Функции в С++

Мы рассмотрели два типа функций, причём объявление функций выполняли в области программы, после подключения заголовочных файлов, но до начала функции main() . Существует несколько способов объявления функций (см. Рисунок 3).

Рисунок 3 — Функции в С++

На рисунке 3 показаны 4 способа объявления функций в языке программирования С++. Рассмотрим структуры объявления функций в одном файле, с главной функцией. Функции можно объявлять в двух областях, до начала функции main() , после функции main() . До сих пор мы объявляли функции в одном файле, перед функцией main() — это самый простой из способов.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" /*область 1 - объявление функций до начала main() место для объявления функций функциям объявленным в этой области не нужны прототипы */ int main(int argc, char* argv) { return 0; }

Если функции объявлять в области 1 , перед главной функцией, то прототипы для этих функций не нужны. Хорошему стилю программирования соответствует способ объявления функций после main() . Рассмотрим структуру такого объявления функций.

// struct_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" // место для объявления прототипов функций int main(int argc, char* argv) { return 0; } /*область 2 - объявление функций после main() место для объявления функций */

// код Code::Blocks

// код Dev-C++

// struct_func.cpp: определяет точку входа для консольного приложения. // место для объявления прототипов функций int main(int argc, char* argv) { return 0; } /*область 2 - объявление функций после main() место для объявления функций */

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

// синтаксис объявления прототипа /*тип возвращаемых данных функции*/ /*имя функции*/(/*параметры функции*/);

Структура объявления прототипа очень схожа со структурой объявления функции. Разработаем программу, которая определяет, является ли введённое пятизначное число палиндромом. Палиндром — это число или текст, который читается одинаково как слева, так и справа: 93939; 49094; 11311.

// palindrom_func.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

// код Code::Blocks

// код Dev-C++

// palindrom_func.cpp: определяет точку входа для консольного приложения. #include using namespace std; bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел int main(int argc, char* argv) { cout << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

В строке 7 объявлен прототип функции нахождения палиндрома пятизначных чисел. Обратите внимание на то, что прототип полностью должен совпадать с заголовком функции, но некоторые отличия все же есть. Например, то, что в прототипе имена параметров перечислять не надо, достаточно объявить их типы данных. В конце объявления прототипа всегда нужно ставить точку с запятой. В остальном объявление прототипа совпадает с объявлением заголовка одной функции. Прототип необходимо объявлять для каждой функции отдельно. Переменная out_number служит для временного хранения введённого числа. В строке 16 в условии оператора выбора if выполняется запуск функции palindrom5() . Аргументом для неё является переменная in_number . функция вернёт значение типа bool , и если функция вернёт true , то условие будет истинно, в противном случае — ложно. Можно было бы сначала присвоить значение, возвращаемое функцией, некоторой переменной, а потом эту переменную подставить в условие оператора выбора if , но это бы увеличило код программы на одну строку. В строках 23 — 40 объявлена функция palindrom5() , с одним параметром, через который функции передаётся пятизначное число. Переменные balance1 , balance2 , balance4 , balance5 объявлены в строке 25 , и необходимы для хранения разрядов пятизначного числа: первого, второго, четвёртого, пятого (нумерация — справа на лево). В строках 26, 29, 32, 35 выполняется одна и та же операция — остаток от деления. Операция остаток от деления отсекает по одному разряду справа налево и сохраняет их в переменные balance1 , balance2 , balance4 , balance5 соответственно. Причём операция остаток от деления чередуется с операцией обычного деления. Операция деления выполняется в строках 27 , 30 , 33 , и уменьшает введённое пятизначное число за шаг на один разряд. В строке 30 операция деления уменьшает введённое число сразу на два разряда, потому, что число пятизначное и средний разряд нас не интересует, он может быть любым. В строках 36 — 39 объявлен оператор выбора if , который сравнивает разряды пятизначного числа, и если они, соответствующим образом, равны, то функция вернёт значение true , иначе — false . Результат работы программы (см. Рисунок 4).

Enter 5zn-e chislo: 12321 Number 12321 - palendrom Для продолжения нажмите любую клавишу. . .

Рисунок 4 — Функции в С++

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

  1. создание файла типа *.cpp, в котором объявляются функции;
  2. создание файлов типа *.cpp и *.h.

К хорошему стилю программирования относится второй способ. Таким образом, если объявлять функции в другом файле, то делать это согласно способу два. Переделаем программу нахождения палиндрома так, чтобы объявление функции palindrom5() находилось в отдельном файле *.cpp . Файл *.h нужен для того, чтобы скрыть реализацию функций, т. е. в файле *.h будут содержаться прототипы функций. С помощью обозревателя решений MVS создаём файл типа *.h и называем его palendrom .

// код файла palendrom.h #ifndef palendrom #define palendrom bool palindrom5(int); // прототип функции нахождения палиндрома пятизначных чисел #endif

Директивы в строках 2,3,5 необходимо всегда объявлять в файлах с прототипами функций, причём прототипы функций всегда объявляются в файлах типа *.h . После директив записанных в строках 2,3 , но до директивы #endif объявляются прототипы функций. В строке 4 объявлен прототип функции palindrom5() . Объявление данной функции находится в файле palendrom.cpp , который предварительно тоже был создан с помощью обозревателя решений MVS.

// содержимое файла palendrom.cpp #include "stdafx.h" #include "palendrom.h" bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел { int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый остаток number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй остаток number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый остаток number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый остаток if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение }

// код Code::Blocks

// код Dev-C++

// содержимое файла palendrom.cpp #include "palendrom.h" bool palindrom5(int number) // функция нахождения палиндрома пятизначных чисел { int balance1, balance2, balance4, balance5; // переменные хранящие промежуточные результаты balance1 = number % 10; // переменной balance1 присвоили первый остаток number = number / 10; // уменьшаем введённое число на один разряд balance2 = number % 10; // переменной balance2 присвоили второй остаток number = number / 100; // уменьшаем введённое число на два разряда balance4 = number % 10; // переменной balance4 присвоили четвёртый остаток number = number / 10; // уменьшаем введённое число на один разряд balance5 = number % 10; // переменной balance5 присвоили пятый остаток if ((balance1 == balance5) && (balance2 == balance4)) return true; // функция возвращает истинное значение else return false; // функция возвращает ложное значение }

В файле palendrom.cpp находится объявление функции palindrom5() . Так как файл palendrom.cpp является исполняемым файлом (*.cpp — исполняемые файлы), то в нём обязательно нужно подключить контейнер "stdafx.h" , как в строке 2 . Чтобы связать файл, где объявлена функция palindrom5() и файл с её прототипом, подключим заголовочный файл (файл с прототипом), это сделано в строке 3 . Обратите внимание на то, что при подключении созданного нами файла используются двойные кавычки, а не знаки больше, меньше. Осталось только запустить функцию palindrom5() в главном исполняемом файле func_palendrom.cpp .

// func_palendrom.cpp: определяет точку входа для консольного приложения. #include "stdafx.h" #include << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

// код Code::Blocks

// код Dev-C++

// func_palendrom.cpp: определяет точку входа для консольного приложения. #include // подключение заголовочного файла, с прототипом функции palindrom5() #include "palendrom.h" using namespace std; int main(int argc, char* argv) { cout << "Enter 5zn-e chislo: "; // введите пятизначное число int in_number, out_number; // переменные для хранения введённого пятизначного числа cin >> in_number; out_number = in_number; // в переменную out_number сохраняем введённое число if (palindrom5(in_number)) // если функция вернёт true, то условие истинно, иначе функция вернёт false - ложно cout << "Number " << out_number << " - palendrom" << endl; else cout<<"This is not palendrom"<

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

  • файл проекта: func_palendrom.cpp
  • заголовочный файл palendrom.h
  • исполняемый файл palendrom.cpp

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

В языке Си все программы рассматриваются как функции. Обычно программы на этом языке состоят из большого числа небольших функций. Для каждой из используемых функций приводится описание и определение функции (Описание функции дает информацию о типе функции и о порядке следования параметров. При определении функции указываются конкретные операторы, которые необходимо выполнить). Функции должны иметь тот же тип, что и значения, которые они возвращают в качестве результатов. По умолчанию предполагается, что функции имеют тип int. Если функция имеет другой тип, он должен быть указан и в вызывающей программе, и в самом определении функции.

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

тип имя_функции ();

Эта спецификация описывает имя функции и тип возвращаемого значения.

Современный стиль используется в конструкциях расширенной версии Си, предложенной ANSI. При описании функций в этой версии Си используются специальные средства языка, известные под названием «прототип функции». Описание функции с использованием ее прототипа содержит дополнительно информацию о ее параметрах:

тип имя_функции (пар_инф1, пар_инф2, …);

где параметр пар_инфi– информация о имени и типе формальных параметров.

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

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

определение параметров;

локальные описания;

операторы;

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

тип имя_функции (пар_инф, пар_инф, …)

где определение параметра пар_инф – содержит информацию о передаваемом параметре: тип и идентификатор.

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

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

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

Результат выполнения функции возвращается при помощи оператора return. Общий вид:

Return(выражение);

Оператор завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Это происходит даже в том случае, если оператор returnявляется не последним оператором тела функции.

Можно использовать оператор returnв виде:

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

{ floaty,x,mult(); /* описание в вызывающей программе */

floatmult(v,k) /* описание в определении функции */

for (res=0.0; k>0; k--)

return(res); } /* возвращает значение типаfloat*/

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

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

[<спецификация КП>][<спецификация типа>]<описатель>([<список параметров>]) [<объявление параметров>] <тело функции>

Спецификация класса памяти <спец. КП> задает класс памяти функции.

<спец. типа> в совокупности с описателем определяет тип возвращаемого значения и имя функции. <Список параметров> представляет собой список (возможно, пустой) имен формальных параметров, значения которых передаются функции при вызове. <Объявления параметров> задают идентификаторы и типы формальных параметров. <Тело функции> составной оператор, содержащий объявления локальных переменных и операторы.

Современная конструкция:

[<спецификация КП>][<спецификация типа>]<описатель>([<список объявлений параметров>])<тело функции>

Объявление функции: классическая форма:

[<спецификация КП>][<спецификация типа>]<описатель>([<список типов аргументов>]);

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

Современный стиль описания (объявление прототипов). В списке типов аргументов прототип может содержать также и идентификаторы этих аргументов.

float f1(float a, float b)

c=(2*pow(a,3)+sin a)/pow(a+b,4);

{ float x,y,s=0;

printf (“\n Введите x, y”);

scanf (“%f %f ”, &x, &y);

s=f1(5.6, y)+f1(2*x-1, x*y);

printf(“\n s=%6.2f ”,s);

Адресные операции. Си поддерживает две специальные адресные операции: операцию определения адреса (&) и операцию обращения по адресу (*). Операция & возвращает адрес данной переменной. Еслиsumявляется переменной типаint, то &sumявляется адресом этой переменной.

Указатели. Указатель является переменной, которая содержит адрес некоторых данных. Вообще говоря, указатель – это некоторое символическое представление адреса. &sumв данном случае означает «указатель на переменнуюsum». Фактическим адресом является число, а символическое представление адреса &sumявляется константой типа указатель. Т.обр. адрес ячейки памяти, отводимой переменнойsumв процессе выполнения программы не меняется.

В языке Си имеются и переменные типа указатель. Значением переменной типа указатель служит адрес некоторой величины. Пусть указатель обозначен идентификатором ptr, тогда оператор следующего вида присваивает адресsumпеременнойptr:ptr=&sum. В этом случае говорят, чтоptr«указывает на»sum. Итак,ptr– переменная, &sum– константа. Переменнаяptrможет указывать на какой-нибудь другой объект:

Значением ptrявляется адрес переменнойmax. Рассмотрим операцию обращения по адресу (*) или операцию косвенной адресации. предположим в переменнойptrсоержится ссылка на переменнуюmax. Тогда для доступа к значению этой переменной можно воспользоваться операцией обращения по адресу (*). Для определения значения, на которое указываетptrзапишем следующий оператор:

Res=*ptr; (Последние два оператора, взятые вместе, эквивалентны следующему:Res=max;)

Использование операции получения адреса и косвенной адресации оказывается далеко не прямым путем к результату, отсюда и появление слова «косвенная» в названии операции.).

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

Описание указателей. При описании переменных типа «указатель» необходимо указать на переменную какого типа ссылается данный указатель. Т.к. переменные разных типов занимают различное число ячеек, в то время как для некоторых операций, связанных с указателями, требуется знать объем отведенной памяти. Примеры правильного описания указателей:

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

{ int x=5, y=10;

printf (“x=%d y=%d\n”, x, y);

change(&x, &y); /*передача адресов функции*/

printf (“x=%d y=%d\n”, x, y); }

int*u, *v; /*uиvявляются указателями*/

temp=*u; /*tempприсваивается значение, на которое указываетu*/

Данная функция изменяет значения переменных xиy. Путем передачи функции адресов переменных х и у мы предоставили ей возможность доступа к ним. Используя указатели и операцию (*), функция смогла извлечь величины, помещенные в соответствующие ячейки памяти, и поменять их местами.

Основная ли тература: 1осн,2осн

Дополнительная литератур а: 10доп

Контрольные вопросы:

1. Могут ли имена формальных и фактических параметров подпрограмм совпадать между собой?

2. В чем состоит отличие описания процедуры и функции?

3. Какие параметры называются формальными и какие – фактическими?

4. Какие существуют стили описания и определения функций?

5. Назовите оператор для возвращения результата выполнения функции?


© 2024
exotop.ru - ExoTop - интернет и технологии