Давыдов В.Г. - Программирование и основы алгоритмизации - 2003
.pdf
|
|
|
|
double |
&Min ) |
; |
|
|
|
|
|
|
|
||
±nt |
main |
( |
void |
) |
|
// |
Возвращает |
0 |
при |
успехе |
|
|
|||
( |
double |
|
|
al = 1.5, |
a2 |
- -17.1, |
Mx, |
Mn; |
|
|
|
||||
|
|
|
|
|
|
||||||||||
|
// Вызов |
функции |
без |
возвращаемого |
|
значения: |
al, |
а2, |
Мх, |
||||||
|
// |
Мп |
- аргументы |
функции |
|
|
|
|
|
|
|||||
|
MaxMln |
( |
al, |
а2, |
Мх, |
Мп ) |
; |
|
|
|
|
|
|
||
|
prlntf( |
|
|
"\п |
al |
= %1д, |
а2 |
= %1д, |
Мх |
= |
%1д, |
Мп = |
%1д |
\п", |
|
|
|
|
|
al, |
а2, |
Мх, |
Мп ) |
; |
|
|
|
|
|
|
|
} |
jretujrn |
О; |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// |
Определение |
|
функции, |
вычисляющей |
Мах:=наиб. |
(Argl,Агд2) |
и |
||||||||
// |
М1п:^наим. |
(Argl,Агд2). |
|
|
Функция |
не |
имеет |
возвращаемого |
|||||||
// |
значения |
|
|
|
|
|
|
|
|
|
|
|
|
||
void |
MaxMin ( |
|
Argl, |
|
// |
|
Исходное |
данное ~ |
передается |
по |
|||||
|
double |
|
|
|
|
|
//значению
double |
Агд2, |
// |
Исходное |
данное |
- передается |
по |
|||
double |
&Мах, |
// |
значению |
по |
ссылке |
|
|||
// |
Ответ |
- |
передается |
|
|||||
double |
ScMin ) |
// |
Ответ |
- |
передается |
по |
ссылке |
|
|
{ |
|
|
|
|
|
|
|
|
|
±f( Argl>Arg2 |
) |
|
|
|
|
|
|
|
|
{ |
= Argl; |
Mln |
= |
Arg2; |
|
|
|
|
|
Max |
|
|
|
|
|
||||
} |
|
|
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
|
|
{ |
= Arg2; |
|
|
|
|
|
|
|
|
Max |
Min |
= |
Argl; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Еще раз напомним, зачем |
нуэюен прототип |
(объявление) |
функ |
ции. Прототип функции используется для контроля правильности вызова функции. В рассмотренном выше примере прототип функции MaxMin применяется компилятором при вызове этой функции Здесь компилятор сравнивает:
•возвращаемое значение функции в прототипе (void - отсутствует) со способом вызова функции (вызов должен начинаться с имени функции);
•сравнивает количество параметров в прототипе и их типы с коли чеством аргументов в вызове функции и типами аргументов.
70
в нашем примере имеет место их полное соответствие, что свидетельствует об отсутствии ошибок в вызове функции. Попутно заметим, что в языке Си прототип функции не обязателен. При его отсутствии компилятор выдает лишь предупреждение о невозмож ности проверить правильность вызова такой функции, что не пре пятствует выполнению программы. Это является недостатком языка Си. В языке же С-ь+, напротив, прототип является обязательным и это хорошо.
Если в файле программного проекта, где находится вызов функции, имеется определение этой функции, причем определение функции предшествует ее вызову, то наличие прототипа не является обязательным. При его отсутствии для контроля правильности вызо ва функции компилятор использует заголовок функции из определе ния функции.
Рассмотрим процесс передачи аргументов а\ и а2 функции MaxMin в приведенной выше программе. Что же функция MaxMin получает в действительности - копии значений аргументов а\ и а2 или значения этих аргументов? В данном случае функция получает копии значений аргументов al и а2. Передача функции копий зна чений аргументов, в противоположность передаче функции значе ний самих аргументов, называется передачей аргументов по значе нию. При таком способе передачи значения аргументов al и а2 ко пируются, на время работы функции, в дополнительную область па мяти и используется в функции в качестве параметров ArgX и Arg2. По завершении работы функции указанная область памяти освобож дается и может быть повторно использована. При этом сами аргу менты al и а2 остаются неизменными (даже, если в теле функции значения параметров будут изменены). Такой способ передачи ар гумента в функцию, по существу, означает "упрятывание" информа ции в функции. Следовательно, он хорош для передачи в функцию исходных данных, которые после завершения функции должны со хранить прежние значения.
В языке C++, в отличие от языка Си, существует и другой спо соб передачи аргумента в функцию - передача аргумента по ссылке. В нашем примере такими аргументами являются Мх и Мп. При пе редаче аргументов Мх и Мп по ссылке в качестве параметров Мах и Min используется сами аргументы Мх и Мп. По завершении работы функции аргументы Мх и Мп останутся такими, какими они были перед завершением функции (в нашем случае Мх получает наиболь шее, а Мп — наименьшее значение из al и а2). Такой способ переда чи аргумента в функцию хорош для получения из функции ответа.
71
в рассмотренном нами примере функция не имела возвращае мого значения. Но ведь существуют и функции, имеющие возвра щаемое значение. Когда же их следует применять? Ответ на этот во прос прост — если из функции получаем единственный ответ. В этом случае удобнее его получать как значение, возвращаемое функцией. Рассмотрим пример, иллюстрирующий такой способ получения от вета. В качестве решаемой задачи рассмотрим более простую зада чу, являющуюся частью только что рассмотренной задачи - запишем прототип, определение и пример вызова функции, определяющей наибольшее значение из двух аргументов. Спецификация соответст вующей функции приведена на рис. 27.
double Arg1 |
Max |
double |
|
double Arg1 |
|||
|
|||
Исходные данные |
Процесс |
Наибольшее из Arg1 и |
|
(передаются по |
|
Arg2 получаем как |
|
значению) |
|
возвращаемое |
|
|
|
значение |
Рис. 27. Спецификация функции с возвращаемым значением
Теперь запишем исходный текст в виде законченной програм мы, содержащий записи прототипа, вызова функции и ее определе ния.
/*
|
Файл |
МАХ.СВР |
|
|
|
проект |
с |
двумя |
функциями. |
Пример |
|||||||
|
Однофайловый |
программный |
|||||||||||||||
иллюстрирует |
работу |
с |
функцией: |
объявление |
(прототип) |
функ- |
|||||||||||
ции, |
|
определение |
функции |
и |
вызов |
|
функции |
с |
возвращаемым |
зна- |
|||||||
чением |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
^include |
|
|
<stdio.h> |
|
// |
Для |
функций |
ввода-вывода |
|
||||||||
// |
Прототип |
функции: |
Argl^ |
Агд2 |
- |
параметры |
функции, |
|
функция |
||||||||
// |
имеет |
возвращаемое |
значение. |
|
В данном случае |
прототип |
|||||||||||
// |
функции |
является |
обязательным, |
так |
как |
вызов |
функции |
||||||||||
// |
выполняется |
|
раньше, |
чем |
функция |
определена |
|
|
|||||||||
dovLble |
Мах ( |
double |
Argl, |
double |
Arg2 |
) ; |
|
|
|
|
|||||||
±пЬ |
main ( |
void |
) |
|
|
// |
Возвращает |
0 |
при |
успехе |
|
|
|||||
{ |
double |
|
|
al |
|
1.5, |
a2 |
= |
-17.1, |
|
Мх; |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|||||||||
|
// |
Вызов |
функции |
с возвращаемым |
значением: |
al, |
а2 |
- |
|||||||||
|
// |
аргументы |
|
функции |
|
|
|
|
|
|
|
|
|
72
Мх = Max( al, |
|
a2 |
) ; |
|
|
printf |
( "\л |
al |
= |
%lg, |
a2 = %lg, Mx = %lg \n", |
|
air |
a2, |
Mx ) |
; |
|
z-etuni |
0; |
|
|
|
|
}
//Определение функции
double |
Max ( |
Arglr |
// |
Возвращает |
наиб. |
(Argl |
,Агд2) |
по |
||
double |
// |
Исходное |
данное |
- |
передается |
|||||
double |
Агд2 ) |
// |
значению |
- |
передается |
по |
||||
// |
Исходное |
данное |
||||||||
( |
|
|
|
// |
значению |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
±£( |
Argl>Arg2 |
) |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
retuim |
|
Argl; |
|
|
|
|
|
|
|
}
re bum Ar g2 ;
}
В рассмотренном примере функция Max имеет возвращаемое значение. Поэтому вызов этой функции должен быть записан в фор ме выражения присваивания. В этом выражении слева от знака опе рации '=' должно указываться имя переменной с типом как тип зна чения, возвращаемого функцией, а справа от знака '=' должно следо вать имя функции. В нашем примере в вызове функции Мах имеет место точное соответствие прототипу этой функции и в части воз вращаемого значения, что также говорит об отсутствии ошибок в использовании функции. В заключение отметим, что класс хранения таких объектов, как параметры функций, передаваемые по значе
нию, называется автоматическим |
(другие названия - |
локальный, |
рабочий). Такое название означает, что область действия |
парамет |
|
ра, передаваемого по значению, ограничивается текущей |
функцией, |
точнее блоком функции за исключением тех вложенных в блок функции блоков, в которых содержится переопределение имени па раметра. Область действия параметра, передаваемого в функцию по ссылке, определяется соответствующим аргументом в вызове функ ции.
Временем эюизни параметра, передаваемого в функцию по значению, является продолжительность исполнения функции. Как только функция завершит работу, значения ее параметров, передан ных по значению, будут утеряны. Время же жизни параметра, пере даваемого в функцию по ссылке, также определяется соответствую щим аргументом в вызове функции. Автоматический класс хранения могут иметь не только параметры функций, но и другие объекты.
73
3.6. Автоматические, регистровые и внутренние статические данные
Автоматические, регистровые и внутренние статические дан ные можно определить внутри любого блока операторов языков Си/С++. Общий синтаксис блока представлен на рис. 28.
{ |
Начало блока |
|
Внутренние определения данных |
|
Операторы |
|
Конец блока |
|
Рис. 28. Общий синтаксис блока |
Отметим разницу в синтаксисе блока языков Си/С++. В языке Си внутренние определения данных блока должны обязательно предшествовать операторам блока, а в языке C++ внутренние опре деления данных и операторы блока могут быть перемешаны. Но при этом необходимо, чтобы использованию внутреннего данного в опе раторе блока обязательно предшествовало его определение.
В качестве примеров блоков, известных нам на данном этапе, можно назвать блоки в операторе //, блоки в циклических операто рах, блоки функций и обычные блоки. Существуют и другие разно
видности блоков, которые будут рассмотрены далее. |
|
|
|||||
Данные можно |
определить |
внутри блока как |
имеющие |
либо |
|||
автоматический auto |
{AUTOmatic), |
либо статический static, либо ре |
|||||
гистровый |
register |
классы хранения |
(рис. 29). По умолчанию, |
когда |
|||
описатель |
класса |
хранения опущен, |
предполагается |
автоматиче |
|||
ский класс |
хранения!!! |
|
|
|
|
74
Определение_внутренних_данных |
|
|
|
|
^ |
Специф._типа |
Идентификатор |
|
|
auto |
|
|
|
|
static |
|
|
|
|
register> |
|
|
|
|
Рис. 29. Определение внутренних данных |
|
|||
Областью |
действия внутренних |
данных |
с классами |
хранения |
автоматический, |
регистровый и внутренний |
статический |
является |
блок, где они определены, включая те вложенные в него блоки, в которых не содержатся переопределения тех же самых имен.
Время эюизни внутренних данных с классом хранения auto и register совпадает со временем выполнения блока. Следовательно, они создаются (размещаются в памяти) в момент входа в блок и уничтожаются при выходе их него. При этом внутренние данные с классом хранения register (они могут иметь только целый тип int) хранятся в быстродействующих машинных регистрах, если это воз
можно, или |
они эквивалентны |
данным с классом |
хранения auto в |
противном случае. |
|
|
|
Время |
снсизни внутренних |
статических данных с классом хра |
|
нения static |
совпадает со временем выполнения |
всей программы. |
Они создаются однократно и сохраняют значения между повторными входами в блок, в котором они определены.
Рассмотрим ряд иллюстрирующих примеров.
Файл |
Р7.СРР |
программный |
проект (файлы Р7.СРР и |
SAVE4.СРР) |
|||
Двухфайловый |
|||||||
с тремя |
функциями. |
Пример |
иллюстрирует |
время жизни |
и |
область |
|
действия |
параметров |
функции |
и внутренних |
автоматических |
дан |
||
ных |
|
|
|
|
|
|
|
V |
|
|
|
|
|
|
|
^include |
|
<stdio.h> |
|||
// Прототипы |
функций |
||||
voxd |
save4 |
( |
float |
) ; |
|
float |
get |
( |
void |
) |
; |
int |
main ( |
void. |
) |
|
|
{ |
|
|
|
|
|
// Для функций |
ввода-вывода |
// Возвращает О при |
успехе |
75
/ / |
Определение |
|
внутренних |
автоматических |
данных. Можно и |
||||||||||
// |
|
в |
такой |
эквивалентной |
форме: |
float |
|
mv^ |
pi; |
|
|||||
auto |
|
float |
/nv, |
|
pi; |
|
|
|
|
|
|
|
|
||
pi |
= |
22. |
Of |
/ |
7; |
^ |
sa\re4 |
( pi |
) ; |
mv = |
get |
( ) |
; |
|
|
|
f ( |
"\n |
mv |
%f pi |
= |
%f"^ |
mv, |
pi |
) ; |
|
|
|
|||
retvLm |
|
0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* |
|
SAVE4.CPP |
|
|
|
|
|
|
|
|
|
|
|
||
Файл |
|
программном |
проекте, |
главная |
функция |
кото |
|||||||||
Используется |
в |
||||||||||||||
рого имеется |
в |
файле |
Р7.СРР |
|
|
|
|
|
|
|
//Прототипы функций
void |
save4( |
float |
) ; |
float |
get( |
void |
) ; |
static |
float |
|
fv; |
void |
save4( |
float |
mv ) |
{ |
fv = mv; |
|
|
|
|
||
|
return; |
|
|
} |
|
|
|
float |
get( |
void |
) |
( • |
|
|
|
} |
return |
fv; |
|
|
|
|
|
|
Внутренняя |
автоматическая переменная wv, определенная в |
функции main, и параметр mv в функции save4 размещены в разных областях памяти и не влияют друг на друга. Первая из них имеет об ластью действия блок функции main и время жизни, равное времени выполнения main. Параметр mv функции save4 имеет в качестве об ласти действия блок этой функции и время жизни - время выполне ния save4.
Автоматические, регистровые внутренние данные и внутрен ние статические данные можно переопределять при вложении бло ков друг в друга, что иллюстрирует следующий пример.
/*
Файл |
Р8.СРР |
|
проект |
с одной |
главной |
функцией. |
|
Однофайловый программный |
|||||||
Пример |
иллюстрирует |
переопределение |
данных |
во вложенных |
бло |
||
ках |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
76
^include |
<stdio.h> |
|
// |
Для |
функций |
ввода-вывода |
|
|||||||||
int main |
( |
void. |
) |
|
// |
Возвращает |
О при |
|
успехе |
|
|
|||||
{ |
|
|
|
|
внутренних |
автоматических |
|
переменных |
||||||||
// Определение |
|
|||||||||||||||
int |
|
|
|
counter^ |
// |
|
Действует |
в |
блоке |
main |
и |
во |
||||
|
|
|
|
i; |
|
// |
|
вложенном |
блоке |
while |
не |
|||||
|
|
|
|
|
// |
Действует |
в |
блоке |
main |
и |
||||||
|
|
|
|
|
|
// |
|
действует |
во |
вложенном |
блоке |
|||||
counter |
= О; 1 = 10; |
// |
|
|
while |
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
||||||
while |
( |
counter |
< i ) |
|
|
|
|
|
|
|
|
|
|
|
||
{ |
// |
Переопределение |
|
внутренней |
|
автоматической |
|
|||||||||
|
во |
|
|
|||||||||||||
|
// |
|
переменной |
вложенном |
блоке |
- |
она |
|
|
|||||||
|
// |
|
располагается |
в |
в |
другой |
|
области |
|
памяти |
|
|
||||
|
// |
|
и |
действует |
блоке |
|
while |
|
|
|
|
|
||||
|
int |
^ |
0; |
i; |
counter++; |
|
|
|
|
|
|
|
|
|
|
|
|
i |
|
^ %d i |
= |
%d", counter, |
i ) / |
||||||||||
|
printf( |
|
"\n counter |
|||||||||||||
} |
f |
( |
"\n\n |
counter |
= |
%ci i |
= |
%d \л", counter, |
i |
) ; |
||||||
retuizn |
0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Результаты выполнения этой программы имеют следующий
вид:
counter |
= 1 |
i |
= О |
|
counter |
^ 2 |
i |
= О |
|
counter |
= 3 |
i |
= О |
|
counter |
= 4 |
i |
== О |
|
counter |
= 5 |
i |
= О |
|
counter |
= 6 |
i |
= 0 |
|
counter |
= 7 |
i |
^ |
0 |
counter |
= 8 |
i |
= 0 |
|
counter |
= 10 |
|
=91=0 |
|
counter |
|
i |
= 0 |
|
counter^ |
= 10 |
|
i |
= 10 |
Как указывалось выше, внутренние статические данные имеют ту же область действия, что и арифметические и регистровые дан ные, но время их жизни максимально и равно времени выполнения программы.
/ / Определение |
функции |
|
|
|
||
void, |
save |
( float |
mv ) |
|
|
|
{ |
Определение |
внутреннего |
статического |
данного с его |
||
// |
||||||
// |
|
инициализацией |
при |
трансляции |
|
|
static |
int counter |
== 0; |
|
|
11
counter++,
return/
}
Значение counter будет сохраняться между вызовами функции save и, следовательно^ по нему можно судить сколько раз вызыва лась эта функция.
3.7.Инициализация данных
вязыках Си/С-1-+ большинство данных может быть явно или
неявно инициализировано в момент их определения. Инициализаци
ей называется присваивание переменной |
начального значения. |
||||||
|
Сводные данные об областях действия, времени жизни и ини- |
||||||
циализируемости объектов Си-программ приведены в табл. 14. |
|||||||
|
|
Табл. 14. Области действия, время жизни |
|
||||
|
|
|
и инициализация объектов |
|
|
||
1 |
Класс |
Внешний |
Внешний |
Параметр |
Автома |
Регист |
Внутренний |
|
хранения |
стати |
функции |
тический |
ровый |
статичес |
|
|
|
Програм |
ческий |
|
|
|
кий |
|
Область |
Файл |
Функция |
Блок |
Блок |
Блок |
|
|
действия |
ма |
|
|
|
|
|
|
Время |
Програм |
Програм |
функция |
Блок |
Блок |
Программа |
|
жизни |
ма |
ма |
Нет |
Все |
|
Все |
|
Инициали- |
Все |
Все |
Все |
|||
|
зируе- |
|
|
|
|
|
|
|
мость |
|
|
|
|
|
|
|
объектов |
|
|
|
|
При каж |
При компи |
[ |
Момент |
При ком |
При ком |
Нет |
При каж |
||
|
инициали |
пиляции |
пиляции |
|
дом входе |
дом вхо |
ляции |
|
зации |
|
|
Нет |
в блок |
де в блок |
|
|
Инициали |
Нулем |
Нулем |
Не |
Не |
Нулем |
|
|
зация по |
|
|
|
определе |
опреде |
|
|
умолча |
|
|
|
но |
лено |
|
|
нию |
|
|
|
|
|
|
|
При отсутствии явных указаний данным с классами |
хранения |
|||||
extern и static присваиваются |
нулевые |
начальные значения. Пере |
численные данные |
и большинство других данных могут быть |
явно |
инициализированы |
в момент определения с помощью указания |
по |
сле их имени знака '=' и константного выраэюения: |
|
static ±nt counter = 0;
// Константное выражение не содержит переменных long max_size = 512 * 200L;
78
Данные с классами хранения extern и static инициализируются однократно в момент компиляции. Автоматические и регистровые данные инициализируются в процессе выполнения программы при каждом входе в блок, в котором они определены.
3.8.Упражнения для самопроверки
1.Что напечатает следующая программа?
^include <stdlo.h>
//Прототипы функций
int |
next |
( |
void |
) |
; |
|
; |
|
|
|
|
|
|
||
tub |
reset |
( |
( |
void |
) |
|
|
|
|
|
|
|
|||
int |
last |
void |
) |
; |
|
|
|
|
|
|
|
|
|||
int |
nw ( |
±nb |
) ; |
|
|
|
|
|
|
|
|
|
|||
int |
|
|
|
|
|
i |
= |
1; |
|
|
|
|
|
|
|
int |
main |
( |
void |
) |
|
|
|
|
|
|
|
|
|
||
{ |
auto |
Int |
|
1, |
j |
; |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
||||||||
|
1 |
= |
reset( |
|
) |
; |
|
|
|
|
|
|
|
|
|
|
fox:( |
j |
= |
3; |
j |
<= 3; |
j++ ) |
|
|
|
|
|
|||
|
{ |
|
prlntf( |
|
|
"1 = %1 j = %l\n", |
i , J |
) |
; |
|
|
||||
|
|
|
|
f ( |
) |
; |
|||||||||
|
|
|
|
"next ( |
)=%l\n"^ |
next |
( |
) |
|||||||
|
|
|
prlntf( |
|
|
"last( |
)=%l\n"r |
last( |
|
) |
) |
; |
|||
|
|
|
|
f( |
"nw (1+j ) =%l\n", |
nw(l+j) |
|
) |
; |
||||||
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
jretujrn |
0; |
|
|
|
|
|
|
|
|
|
|
|||
static |
Int |
|
|
|
1=10; |
|
|
|
|
|
|
||||
int |
next |
( |
void |
) |
|
|
|
|
|
|
|
|
|
||
{ |
T&txuon |
1 |
|
+= |
1; |
|
|
|
|
|
|
|
|
||
} |
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int |
last |
( |
void |
) |
|
|
|
|
|
|
|
|
|
||
{ |
return |
1 |
|
-= |
1; |
|
|
|
|
|
|
|
|
||
} |
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int |
nw ( |
Int |
|
1 |
) |
|
|
|
|
|
|
|
|
|
79