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

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
PAGE 86
Функция это относительно независимая программная единица, выполняющая определенные действия. Все языки программирования имеют определенный набор библиотечных функций, а также позволяют программисту разрабатывать собственные функции. Функции программиста реализуют модульный принцип построения программ. В данной лабораторной работе осваивается работа с готовыми функциональными модулями, а также разрабатывается ряд собственных функций.
Программа на любом языке программирования может состоять из одной главной программы, но чаще включает в себя главную программу и несколько подпрограмм. Главная программа осуществляет ввод входных данных, организует работу основного алгоритма, вызывает другие подпрограммы и осуществляет вывод результата. Другие подпрограммы выполняют конкретные задачи. Их создает программист в процессе разработки программы, реализуя модульный принцип построения программной структуры. Определенные действия, реализуемые группой операторов, выделяются в отдельную подпрограмму-модуль в следующих случаях:
Любая правильно спроектированная подпрограмма должна обладать следующими признаками:
В ряде языков программирования существует четкое разделение подпрограмм на функции и процедуры, например, function и procedure в языке паскаль. Это разделение основано на том, имеет ли подпрограмма возвращаемое значение, которое принимается от нее, как в случае использования встроенных математических функций: sin(), cos(), sqrt() и др. В языке С/С++ все подпрограммы называются функциями. Даже главная программа также является функцией со стандартным названием main.
Функция в языке С/С++ состоит из заголовка и тела функции, окаймленного фигурными скобками.
<заголовок>
{
<тело функции>
}
Заголовок имеет следующую структуру:
<тип> <идентификатор>(<список формальных параметров>)
Элемент заголовка <тип> указывает тип возвращаемого функцией значения: int, double, char и др. Для функции, не имеющей возвращаемого значения (аналог процедуры в паскале) указывается тип void.
Идентификатор (имя) функции подчиняется тем же формальным правилам, что и идентификаторы переменных в С/С++. Однако принято, что он обязательно должен иметь мнемонический смысл, т.е. указывать на действия, выполняемые функцией. Кроме того, он обычно длиннее, чем идентификаторы переменных.
Список формальных параметров это список входных и выходных переменных функции с указанием их типов. В отличие от паскаля, название типа указывается для каждой переменной и перед ее идентификатором, например:
void func(char c, int k, double x, int& m, double& s)
{
...
}
Выходные данные функции имеют указатель типа, заканчивающийся знаком & - амперсанд. В списке параметров могут использоваться идентификаторы массивов и структур, имена других функций, а также указатели на них.
Любой язык программирования имеет механизм вызова функций и механизм обмена данными с ней. Вызов функции осуществляется по-разному, в зависимости от того, имеет ли она возвращаемое значение или нет. Если функция имеет возвращаемое значение, то оно используется как операнд, т.е. может присваиваться переменной, участвовать в операции или служить аргументом другой функции, например:
void main()
{
...
X=func(...);
...
...+2*func(...)+...;
...
...+sqrt(func(...))...
...
}
Если функция не имеет возвращаемого значения (тип void), то она вызывается следующим образом:
void main()
{
...
func(...);
...
}
В языке C++ компилятор имеет опцию “Разрешить использование функции, как процедуры”, по умолчанию установленную как «да». При этом не диагностируется как ошибка или предупреждение вызов функции с возвращаемым значением вторым способом. Но этого рекомендуется не делать и всегда, когда функция имеет возвращаемое значение, принимать его, присваивая какой-либо переменной, и использовать содержащуюся в нем информацию.
Передача данных в функцию и из функции через список параметров осуществляется следующим образом. При вызове функции указывается список фактических параметров, число, порядок и типы которых должны соответствовать списку формальных параметров в заголовке функции. В общем случае, в списке фактических параметров могут быть переменные, константы и выражения соответствующих типов. Например, для функции с уже приводимым заголовком
void func(char c, int k, double x, int& m, double& s)
{
...
}
могут быть реализованы следующие вызовы:
void main()
{
char a;
int i,m,n;
double r,t,y;
...
func(a,i,t, m,y);
...
func(+,5,-3.45, n,y);
...
func($,i+2,sin(x+M_PI)+1, n,r);
...
}
При вызове функции в списках фактических параметров указываются только идентификаторы, указания типов недопустимо и является синтаксической ошибкой.
Передача данных осуществляется по соответствию мест элементов в списках фактических и формальных параметров. Она осуществляется двумя способами: по значению и по ссылке. Передача по значению происходит, если в списке формальных параметров его тип не помечен символом &. Она осуществляется следующим образом. В функции создается внутренняя переменная, которой присваивается значение соответствующего элемента списка фактических параметров. При этом в списке фактических параметров могут быть переменная, константа или выражение соответствующего типа (см. первые три параметра в приведенном выше примере). Данные передаются только в функцию. Если какая либо переменная из списка формальных параметров при работе функции изменяется (например k), то соответствующая ей переменная i в главной функции (при первом вызове) остается неизменной.
Передача по ссылке происходит, если тип переменной в списке формальных параметров помечен знаком &. При этом в функцию передается адрес ячейки памяти соответствующей переменной из списка фактических параметров и функция работает с этой же самой ячейкой. Если значение хранящейся в ней переменной изменяется в функции, то оно изменяется и у соответствующей переменной списка фактических параметров. Поэтому выходные переменные функции всегда передаются по ссылке. Понятно, почему в списке фактических параметров на этих местах не могут быть константы или выражения.
Еще раз подчеркнем, что передача данных осуществляется по месту в списке, а не по идентификаторам. Например, при вызове следующей функции
void func(double x, double y)
{
...
}
void main()
{
double x,y;
...
func(y,x);
...
}
переменной x в функции func() будет передаваться значение переменной y из функции main(). И наоборот, переменной y в функции func() будет передаваться значение переменной x из main().
Язык С/С++ допускает определенные вольности в соответствии типов фактических и формальных параметров при передаче по значению. Возможно, например, что в списке фактических параметров находится переменная вещественного типа, а в списке формальных параметров ей соответствует переменная целого типа. При этом произойдет искажение данных при передаче дробная часть просто потеряется. В паскале такое несоответствие диагностирует компилятор, а в С/С++ за этим должен следить программист. Допустима только передача данных целого типа переменным вещественного, а не наоборот.
В языке С/С++ имеется возможность задания значений параметров функций по умолчанию. Обычно это используется для параметров, которые чаще имеют стандартные значения и изменяются сравнительно редко. В заголовке функции им присваиваются эти значения, как показано в следующем примере (переменные key и s):
void func(double a, double b, int key=0, double s=2.5)
{
...
}
Вызов этой функции возможен тремя способами:
void main()
{
...
func(a,b,key,s);
...
func(a,b,key);
...
func(a,b);
...
}
В первом случае все четыре формальных параметра функции a, b, key и s получат значения от фактических, во втором случае - a, b и key получат значения от фактических, а s примет значение по умолчанию, равное 2.5. В третьем случае значения по умолчанию примут обе переменные из конца списка: key=0 и s=2.5. Параметры, значения которых присваиваются по умолчанию, обязательно должны располагаться в конце списка формальных параметров и могут использоваться, начиная с конца и последовательно. Так в приведенном примере нельзя переменной s задать значение, а key использовать по умолчанию.
Часто входными или выходными параметрами функции являются массивы, одномерные, двумерные или с большим числом индексов. В языке С/С++ массивы не могут быть возвращаемым значением функции и передаются только через список параметров. Массивы всегда передаются по ссылке, поэтому знак & в списках выходных параметров является излишним. При этом следует помнить, что изменение входного массива в функции приведет к его изменению в вызвавшем модуле.
Заголовок функции, через список параметров передается одномерный массив X, может выглядеть, как показано в следующем примере:
void func(int n, double X[])
{
...
}
Скобки после идентификатора массива обычно оставляются пустыми. Они нужны, чтобы указать компилятору на то, что это - массив, а не простая скалярная переменная. Фактически в функцию передается адрес нулевого элемента массива (указатель на массив). В главной функции передаваемый массив должен быть описан с указанием числа его элементов через константу. Идентификаторы, как и в случае простых переменных, могут быть различными:
void main()
{
int m;
double Y[100];
...
func(m,Y);
...
}
В списке фактических параметров при вызове функции указывается только идентификатор массива, квадратные скобки и указатель типа недопустимы.
В случае передачи двумерного массива дело обстоит несколько сложнее. В заголовке функции могут быть оставлены пустыми только первые скобки, а вторые должны заключать константу, причем такую же, что использована при описании соответствующего массива в главной функции:
void func(int n, int m, double A[][10])
{
...
}
void main()
{
int m,n;
double B[20][10];
...
func(m,n,B);
...
}
Константа во вторых скобках нужна для того, чтобы преобразовать индексы строки i и столбца j в функции в величину смещения указателя k относительно начала массива:
,
где constN константа, использованная при описании массива во вторых скобках (в приведенном выше примере 10).
Необходимость указания этой константы в заголовке функции делает последнюю недостаточно универсальной функция оказывается привязанной к конкретному размеру массива. Удобным выходом из этой ситуации представляется следующий. Язык С/С++, как и паскаль, позволяет создавать новые типы данных на основе уже существующих. Введем тип Matrix для двумерного массива следующим описанием:
typedef double Matrix[10][10];
которое поместим в отдельный заголовочный файл define.h. Тогда, подключая этот заголовочный файл перед главной функцией main() и перед функцией func(), используем тип Matrix, который исключает явное использование констант в заголовке функции:
#include “define.h”
void func(int n, int m, Matrix A)
{
...
}
void main()
{
int m,n;
Matrix B;
...
func(m,n,B);
...
}
В случае необходимости размер массива изменяется в одном месте заголовочном модуле define.h, при этом он одновременно изменяется во всех используемых функциях. Для однообразия в этом заголовочном файле описан и тип одномерного массива Vector:
typedef double Vector[10];
и некоторые другие типы. Заголовочный файл define.h в данной работе дается готовым. Он должен располагаться в том же рабочем каталоге, что и все остальные файлы, с которыми в данный момент работает программист. Имя его в директиве #include указывается в кавычках, а не в угловых скобках. Чтобы избежать повторного описания типов, этот заголовочный файл снабжен директивами условной компиляции для препроцессора.
Довольно часто возникает необходимость передать в функцию имя другой функции. Например, для функции, вычисляющей интеграл, указать имя функции, реализующей подынтегральную. В списке формальных параметров описывается указатель на функцию. Например, необходимо передать указатель на вещественную функцию вещественного аргумента, имеющую заголовок:
double FuncName(double x)
{
...
}
Указатель на такую функцию прописывается в списке формальных параметров следующим образом:
void func(..., double (*FuncName)(double), ...)
{
...
}
В главной функции в функцию func() пусть необходимо передать имя другой функции f(). Это делается очень просто:
double f(double x)
{
...
}
void main()
{
...
func(..., f, ...);
...
}
Указатель на определенный тип функций также может быть описан в модуле define.h:
typedef double (*f_x) (double); ,
тогда тип f_x можно использовать в заголовке функции, куда передается имя функции этого типа:
void func(..., f_x FuncName, ...)
{
...
} ,
что является гораздо более удобным.
Функция может быть расположена в том же файле, что и вызывающая ее (main() или какая либо другая), а может быть и в другом. Причем, в последнем случае, она может быть откомпилирована и находиться в объектном файле name.obj.
Если функция расположена в том же файле, что и вызывающая ее (main()или другая), то она видима, если расположена по тексту ранее:
void func(char c, int k, double x, int& m, double& s)
// вызываемая функция
{
...
}
void main()
// главная функция
{
char a;
int i,m;
double r,t,y;
...
func(a,i,t, m,y);
...
}
Обычно такое расположение считается неудобным, т.к. вызывающая функция имеет более высокий иерархический статус, а расположена ниже. Поэтому вызываемую функцию располагают по тексту ниже, а перед заголовком вызываемой функции прописывают ее прототип. Прототип это фактически заголовок функции, в котором можно не приводить идентификаторы, а только указывать типы формальных параметров. Прототип обязательно заканчивается точкой с запятой:
void func(char, int, double, int&, double&); // прототип
void main()
// главная функция
{
char a;
int i,m;
double r,t,y;
...
func(a,i,t, m,y);
...
}
void func(char c, int k, double x, int& m, double& s)
// вызываемая функция
{
...
}
Если вызываемая функция находится в другом файле, то ее прототип также обязательно должен находиться перед вызывающей функцией.
Для соединения функций, находящихся в разных файлах, производится их объединение в проект. Действия по созданию проекта зависят от используемой среды программирования и поэтому в данном руководстве не описываются. Их следует изучить по отдельному описанию.
Следует отметить, что интегрированная среда программирования отслеживает производимые изменения в текстах во всех открытых cpp-файлах и автоматически их перекомпилирует при запуске программы командой Run.
Альтернативным способом использования функций, находящихся в других файлах, служит их подключение в cpp-форме директивой
#include filename.cpp.
Однако этот способ является менее универсальным и, в некоторых случаях, приводит к ошибкам повторного описания объектов программ. Основным способом следует рекомендовать объединение файлов в проект.
Examples \ 08 Готовые модули и входящими в них функциями.
и
и
методом Ньютона с помощью функции RootNewton().