Программирование с использованием подпрограмм



Nbsp; Ю. Е. Алексеев, А. В. Куров   Практикум по программированию на языке C в среде VS C++   Часть 3   Москва Издательство МГТУ им. Н. Э. Баумана 2013 УДК 681.3.06(075) ББК 22.18 А47 Рецензенты А.Д.Козлов, А.И.Коротаев Алексеев Ю.Е., Куров А.В. А47 Практикум по программированию на языке C в среде VS C++. Часть 3: Учеб. посо-бие. –М.: МГТУ им. Н.Э.Баумана, 2013.- с.   В данном пособии продолжается изложение основ программирования на языке C в среде VC C++.      В данной части пособия рассматривается программирование с использованием подпрограмм. Изложены принципы организации функций в языке C, подробно рассматриваются вопросы передачи параметров в подпрограммы. Большое внимание уделено особенностям и трудностям передачи в подпрограммы в качестве параметров массивов. Изложение теоретических положений сопровождается практическими примерами программ. Приведены комплекты заданий (по 25 вариантов) на составление программ с подпрограммами. В пособии рассматривается также организация рекурсивных, перегружаемых функций, подпрограмм с переменным числом параметров, а также с параметрами по умолчанию, использование шаблонов функций. Приведен материал, связанный с использованием динамических массивов. Во второй части пособия рассмотрено создание пользовательских библиотек функций (модулей). Материал пособия авторы используют при проведении практических занятий в МГТУ им.Н.Э.Баумана. Для студентов первого курса машино- и приборостроительных специальностей. Может быть полезно преподавателям как сборник заданий при проведении лабораторных работ.    Оглавление 6. Программирование с использованием подпрограмм.. 5 6.1. Объявление функций. 5 6.2.Глобальные переменные. 8 6.3.Передача параметров. 10 6.4.Передача массивов в качестве параметров. 12 6.5. Примеры выполнения заданий. 17 6.6. Задания А для самостоятельной работы.. 23 6.7. Задания Б для самостоятельной работы.. 27 6.8. Рекурсивные функции. 30 6.9. Пример выполнения задания на составление рекурсивной функции. 33 6.10. Задания для самостоятельной работы.. 35 6.11.Дополнительные сведения о подпрограммах и массивах. 40 Динамические массивы.. 40 Перегружаемые функции. 45 Параметры со значениями по умолчанию.. 46 Функции с переменным числом параметров. 47 Шаблоны функций. 49 Передача имен функций в качестве параметров. 50 6.12. Примеры программ с подпрограммами. 52 7. Модули пользователей. 58 7.1.Создание и использование модулей. 58 7.2. Пример выполнения задания на составление модуля. 69 7.3. Задания для самостоятельной работы.. 75 7.4. Пример выполнения задания. 81 7.5. Задания для самостоятельной работы.. 87 Список литературы.. 93 Алфавитный указатель. 94 Вопросы для самопроверки. 95 Введение Данное пособие представляет собой третью часть пособия, посвященного изложению основ программирования и решения основных инженерных задач на языке C в среде VS C++ . В данной части рассматриваются принципы организации подпрограмм в языке C. Этот раздел имеет важное значение при изучении основ программирования на данном языке, т.к. любая программа на языке C представляет собой совокупность подпрограмм. В частности основная подпрограмма также представляет собой подпрограмму. Авторы знакомят читателя с принципами организации подпрограмм. Большое внимание уделяется вопросам передачи параметров между функциями. Этот материал обычно вызывает у студентов существенные затруднения, т.к. передача параметров связана с использованием указателей. Работа с указателями требует определенных навыков и опыта программирования, а они как раз и отсутствуют у начинающих программистов. В пособии рассмотрены основные способы передачи параметров - по значению и по адресу. Теоретические положения иллюстрируются примерами программ, что существенным образом облегчает восприятие материала. Особое внимание авторы уделили передаче массивов в качестве параметров функций. Рассмотрены возможные способы передачи и обработки двумерных массивов. Авторы привели ряд программ с использованием функций, что позволит студентам глубже разобраться в проблеме организации подпрограмм и успешнее справиться с написанием программ, задания на составление которых приведены в конце каждого раздела. В пособии представлен также материал, касающийся организации и использования рекурсивных функций. Приведен комплект заданий на составление рекурсивных функций вычисления значений некоторых определенных интегралов. Данные задания целесообразно использовать во втором семестре, когда студенты в курсе математического анализа изучают раздел интегрирования. При написании функций обработки массивов целесообразно использовать динамические массивы, что позволяет сделать такие подпрограммы максимально универсальными. В связи с этим авторы включили в данное пособие материал, содержащий сведения о динамических массивах, стандартных функциях и операциях выделения и освобождения памяти. Рассмотрена организация перегружаемых функций, позволяющих создавать ряд одноименных функций, но различающихся типом и/или количеством параметров. При выполнении многократных вычислений с параметрами, часть из которых не изменяется, удобным является использование параметров по умолчанию. В связи с этим авторы рассмотрели организацию функций с параметрами по умолчанию. В пособии рассмотрены также и другие удобные возможности языка C, как функции с переменным числом параметров и шаблоны функций. Шаблоны функций позволяют использовать одну т ту же функцию для обработки данных различающихся типов. Во второй части пособия излагается материал, посвященный созданию библиотек подпрограмм пользователей (модулей). Рассмотрены правила организации модулей, их структура, действия, которые необходимо выполнить при работе в среде VS C++. Как и в первых двух частях, в данном пособии краткое изложение теоретического материала сопровождается достаточным количеством примеров, что должно позволить студенту за ограниченное время освоить правила организации подпрограмм в языке C. Для закрепления изучаемого материала и приобретению необходимого опыта разработки программ с подпрограммами авторами предлагаются по каждой теме комплекты индивидуальных заданий, выполнение которых послужит приобретению навыков и умений при решении рассматриваемых задач. Задания имеют индивидуальный характер, могут использоваться при проведении лабораторных работ, подготовке к рубежным контролям знаний, а также в качестве заданий при проведении контрольных мероприятий.  

Программирование с использованием подпрограмм

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

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

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

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

 

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

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

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

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

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

Тип Имя (Список)

где

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

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

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

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

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

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

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

return выражение;

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

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

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

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

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

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

В качестве простейшего примера организации функции приведем пример объявления и вызова функции, возвращающей первое из двух значений, представленных параметрами, кратное 5. Если оба значения не кратны 5, то функция возвращает -1.

// простейшая функция

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

int mod5(int k, int m);//объявление функции

int _tmain(int argc, _TCHAR* argv[])

{ int a,b;

printf("wwedite a i b\n");

scanf("%d%d",&a,&b);

printf("\n a=%4d b=%4d",a,b);

printf("\nperwoe kratnoe pjati = %4d",mod5(a,b));//1

printf("\nperwoe kratnoe pjati = %4d",mod5(a+9,b+2));//2

printf("\nperwoe kratnoe pjati = %4d",mod5(a+7,b+3));//3

 

getch();

return 0;

}

 

int mod5(int k, int m)//определение функции

// k,m - формальные параметры

{

if (k%5==0)

return k;

else

if(m%5==0)

return m;

else

return -1;

 }

 

Рассмотрим для конкретных значений переменных a=1 и b=2, что будет выведено на экран приведенной программой:

1) будет выведено значение -1, т.к. ни 1, ни 2 не делятся на 5 без остатка;

2) будет выведено значение 10,т.к. 1+9=10 и это число делится без остатка на 5, возвращаемое значение параметра k получает именно значение 10;

3) будет выведено значение 5, т.к. первое значение (1+7) на 5 не делится без остатка, а второе значение (2+3) делится без остатка на 5, возвращаемое значение параметра m как раз получает значение 5.

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

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

Глобальные переменные

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

// функция без параметров

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

const int mmax=10,nmax=15;

float a[mmax][nmax], b[mmax*nmax],sum,pr;

int m,n,k;

void fun();

int _tmain(int argc, _TCHAR* argv[])

{ int i,j;

printf("wwedit m i n\n");

scanf("%d%d",&m,&n);

printf("\nwwedie matrizu\n");

for(i=0;i<m;i++)

 for (j=0;j<n;j++)

   scanf("%f",&a[i][j]);

fun();

if (k>0)

{printf("\nmassiw poloshitelnix\n");

for(i=0;i<k;i++)

       printf("%6.2f ",b[i]);

} else printf("\npoloshitelnix net");

printf("\n sum=%6.2f pr=%6.2f",sum,pr);

getch();

return 0;

}

void fun()

{int i,j;

 k=0;

 sum=0.0;

 pr=1.0;

 for(i=0;i<m;i++)

 for (j=0;j<n;j++)

 {sum+=a[i][j];

  pr*=a[i][j];

if (a[i][j]>0)

{b[k]=a[i][j];

k++;

 }

}

}

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

Следующая программа демонстрирует побочный эффект. Результат работы зависит от последовательности вычисления суммы, сохраняемой в переменной y. В первом варианте сначала переменной y присваивается значение переменной x, а затем дважды производится обращение к функции sum, в итоге получим y=22. Во втором варианте сначала переменной y присваивается сумма значений, возвращаемых при двух обращениях к функции sum, а затем прибавляется значение переменной x. Поскольку значение глобальной переменной x изменяется в функции, то к y прибавится дважды измененное значение x, равное 9 и результат будет равен 26, а в первом случае в y заносится исходное значение x (до обращения к функции), результат будет равен 22.

// побочные эффекты

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

#include "math.h"

float x=5;

float sum(float a,float b);

int _tmain(int argc, _TCHAR* argv[])

{ float a=8,b=2,y=0;

printf("x=%5.1f",x);

y=x; // 1-ый вариант

y+=sum(a,b)+sum(3,4);

//2-ой вариант y+=x;

printf(" y=%5.1f",y);

getch();

return 0;

}

float sum( float a,float b)

{ x=x+2;

printf("\nx=%5.1f a=%5.1f b=%5.1f",x,a,b);

return a+b;

}

Передача параметров

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

Существуют два способа передачи – по значению и по адресу.

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

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

Если требуется запретить изменение внутри функции параметра, передаваемого по адресу, то используется модификатор const перед его описанием в заголовке функции. Такое объявление облегчает отладку программ, так как в этом случае легко определить, какие величины можно изменять внутри функции, а какие нет. Кроме того, при передаче параметров параметра по адресу с модификатором const в качестве фактического параметра можно использовать выражение, если оно допустимо типом параметра. Так, для библиотечной функции printf, имеющей заголовок int printf(const char *_Format,…), в качестве первого фактического параметра можно задавать не только строковую константу, например, "%f%f", но и строковую переменную с таким значением или склейку строк, приводящую к такому же значению. Справка: заголовок любой функции в тексте программы можно увидеть во всплывающей подсказке, подведя курсор мыши к её имени.

Модификатор const можно использовать и для параметров-значений. Это может потребоваться в тех случаях, когда из этой подпрограммы можно будет вызывать другую подпрограмму, в которой данный формальный параметр без каких-либо изменений должен представлять фактический параметр или входить в выражение, представляющее такой фактический параметр. Очевидно, эта вызываемая подпрограмма не должна изменять значение формального параметра-константы вызывающей подпрограммы.

Подводя итог, можно также сказать: в объявлениях и заголовках подпрограмм желательно использовать модификатор const даже в тех случаях, когда не вникая в суть её назначения и алгоритма надо знать, возможно ли изменение значения в ячейке памяти стека для параметра-значения и должно ли быть или нет изменение фактического параметра при передаче по адресу т.е. является ли параметр только входным (если задан с const), или только выходным и, возможно, также входным (если задан без const).

Приведем пример функции void, возвращающей через параметры-указатели в качестве результата сумму и произведение двух чисел, представленных параметрами-значениями

// передача параметров по адресу

#include "stdafx.h"

#include "stdio.h"

#include "conio.h"

 

void sumpr(float a,float b,float *s,float *pr);

int _tmain(int argc, _TCHAR* argv[])

{ float c,d,sum,pr;

printf("wwedite dwa chisla\n");

scanf("%f%f",&c,&d);

sumpr(c,d,&sum,&pr);//передача адресов переменных sum и pr, являющихся

                 //результатами

printf("\nsumma=%6.2f proiswedenie=%6.2f",sum,pr);

getch();

return 0;

}

void sumpr(float a,float b,float *s,float *pr)

{ *s=a+b;

*pr=a*b;

}

 


Дата добавления: 2018-10-27; просмотров: 553; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!