Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Давыдов В.Г. - Программирование и основы алгоритмизации - 2003

.pdf
Скачиваний:
839
Добавлен:
13.08.2013
Размер:
9.55 Mб
Скачать

 

 

 

 

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

( )

;

 

 

print

 

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

) ;

print

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 (

)

;

 

 

 

print

 

"next (

)=%l\n"^

next

(

)

 

 

 

prlntf(

 

 

"last(

)=%l\n"r

last(

 

)

)

;

 

 

 

print

 

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