У вас вопросы?
У нас ответы:) SamZan.net

Лабораторная работа 6 Функции программиста.

Работа добавлена на сайт samzan.net: 2016-03-13

Поможем написать учебную работу

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

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

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 7.4.2025

PAGE  86

Лабораторная работа № 6

Функции программиста. Разработка программ с функциями

1. Введение

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

2. Принципы модульной организации программ

Программа на любом языке программирования может состоять из одной главной программы, но чаще включает в себя главную программу и несколько подпрограмм. Главная программа осуществляет ввод входных данных, организует работу основного алгоритма, вызывает другие подпрограммы и осуществляет вывод результата. Другие подпрограммы выполняют конкретные задачи. Их создает программист в процессе разработки программы, реализуя модульный принцип построения программной структуры. Определенные действия, реализуемые группой операторов, выделяются в отдельную подпрограмму-модуль в следующих случаях:

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

Любая правильно спроектированная подпрограмма должна обладать следующими признаками:

  •  легко формулируется, что она делает;
  •  она имеет четкую структуру входных и выходных данных (но в некоторых случаях, входные, выходные или те и другие данные могут отсутствовать, вспомним, например, функцию clrscr());
  •  внутренняя сложность функции выше сложности ее связей со внешней средой, осуществляемой через входные и выходные данные. Как говорят программисты Функция снаружи проще, чем внутри.

В ряде языков программирования существует четкое разделение подпрограмм на функции и процедуры, например, function и procedure в языке паскаль. Это разделение основано на том, имеет ли подпрограмма возвращаемое значение, которое принимается от нее, как в случае использования встроенных математических функций: sin(), cos(), sqrt() и др. В языке С/С++ все подпрограммы называются функциями. Даже главная программа также является функцией со стандартным названием main.

3. Организация функций в языке С/С++

Функция в языке С/С++ состоит из заголовка и тела функции, окаймленного фигурными скобками.

<заголовок>

{

<тело функции>

}

Заголовок имеет следующую структуру:

<тип> <идентификатор>(<список формальных параметров>)

Элемент заголовка  <тип>  указывает тип возвращаемого функцией значения: 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 использовать по умолчанию.

4. Передача массивов в списке параметров

Часто входными или выходными параметрами функции являются массивы, одномерные, двумерные или с большим числом индексов. В языке С/С++ массивы не могут быть возвращаемым значением функции и передаются только через список параметров. Массивы  всегда передаются по ссылке, поэтому знак & в списках выходных параметров является излишним. При этом следует помнить, что изменение входного массива в функции приведет к его изменению в вызвавшем модуле.

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

5. Передача имен функций в списке параметров

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

 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, ...)

 {

...

}  ,

что является гораздо более удобным.

6. Видимость функций

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

Однако этот способ является менее универсальным и, в некоторых случаях, приводит к ошибкам повторного описания объектов программ. Основным способом следует рекомендовать объединение файлов в проект.

7. Задание к работе

  1.  Изучить и опробовать программы  Examples \ 07 Функции  из подразделов Основы и Передача массивов.
  2.  На основе разработанной во второй лабораторной работе программы расчета корней кубического уравнения разработать соответствующую функцию и управляющую  программу к ней. Функция и управляющая программа должны находиться в разных файлах.
  3.  На основе разработанной в четвертой лабораторной работе программы для расчета среднего значения и среднеквадратичного отклонения элементов одномерного массива разработать соответствующую функцию и управляющую  программу к ней. Функция и управляющая программа должны находиться в разных файлах.
  4.  На основе одной из разработанных в пятой лабораторной работы программы (по указанию преподавателя)  разработать соответствующую функцию и управляющую  программу к ней. Функция и управляющая программа должны находиться в разных файлах.
  5.  Ознакомиться с составом модулей каталога

Examples \ 08 Готовые модули и входящими в них функциями.

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

  1.  Разработать управляющую программу для расчета определенных интегралов с помощью функции SdxSimps() (модуль integral.cpp). Рассчитать интегралы

      и       

  1.  Разработать управляющую программу для решения уравнений

и  

методом Ньютона с помощью функции RootNewton().




1. Стратегический анализ диверсифицированных компаний
2. Организация производства подсолнечника и ее совершенствование в учхозе УГСХА Чердаклинского района Ульяновской области
3. Реферат- Таможенные режимы
4. дипломная практика Сестринское дело в терапии Защищена Ф
5. Шаманизм
6. Петербург 2011 Авторсоставитель С
7. Диагностика психологической готовности к школьному обучению детей с речевыми нарушениями
8. ии п-пм Прогноз соцэкго развития ~ стратегия развития взгляд в будущее
9. Zur Genelogie der Morl Полемическое сочинение
10. Пророк Алмустафа избранный и возлюбленный заря своего дня двенадцать лет ждал в городе Орфалесе возвр