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

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

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

{

static int j = 5; rGtuJcn 1 = j += 1;

/ k k k k k k k k k k k k - k k k k k k - k k k k k фз^р[Л 3

k k k k k k k k k k k k k k k k k k k k k k k k k k k k /

extern Int

i /

 

±nt reset ( void. )

{

jretuxn i/

;

2. Что напечатает следующая программа?

^include <std±o.h>

// Прото типы функций int next ( int );

int reset ( void. ) ; int last ( int ) ;

int

i = 2;

 

 

 

 

 

int main ( void

)

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

auto

int

i,

j ;

 

 

 

 

 

i = reset (

);

 

 

 

 

 

£or(

j = 1; j <= 2; j++ )

 

 

 

J

 

 

 

 

 

 

 

 

printf(

 

"\ni = %d j

= %d\n"r i,

j );

 

print f(

"next ( i

)

= %d\n", next ( i

)

);

printf(

 

"last( i

) = %d\n'\ last

( i

)

);

}

return 0;

}

/ / k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k - k k k k k k k k k k k k k - k k k k k k k k k

int reset( void )

{

return ++i;

}

/ / k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k

int next ( int j )

{

return j = i++;

}

/ / k k - k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k k

int last ( int j )

{

static int i = 10;

80

return j = i - - /

i

Как уже указывалось, ответы для этих упражнений можно про­ верить в разд. 18.

3.9Производные типы данных

вязыках Си/С++ предусмотрены несколько производных ти­

пов данных, среди которых основными являются:

массивы;

структуры;

объединения.

3.9.1. Массивы

Структура данных, называемая массивом, позволяет опреде­ лить непрерывный (по отношению к расположению в памяти) набор однотипных объектов данных.

Приведем пример определения массива:

Этот массив

символьного

типа

способен

хранить 15

символов.

Начальные

значения

элементов

не

определены:

определение

мас­

сива дано

без

 

инициализации

 

 

 

 

 

V

 

kaf__name [ 15

];

 

 

 

 

char

 

 

 

 

 

Индивидуальный доступ к отдельным элементам массива осу­

ществляется с помощью индексированных

имен:

kaf_name[

О ] . . .

kaf_name [ 14 ]

Обратите внимание, что индексы элементов массива изменя­ ются в диапазоне О ... 14, а не в диапазоне 1 ... 15.

Массивы могут иметь любой класс хранения, кроме register. Области действия и времена жизни массивов такие же, как и у про­ стых данных. Массивы моэюно инициализировать (рис. 30):

chstr

kaf_name[

15

]

= {

'К',

'а',

'ф\

' е \

'д'г

 

'Р\

' а ' ,

'

Ч

'АЧ

^B^,

' Г ' ,

^\0^

} /

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

81

По этой причине массив kafjname

может хранить в виде стро­

ки название кафедры, состоящее не более чем из 14 символов!

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

kaf_name | к

а

Ф е

Д

Р

а

 

А

В

Т

\0

?

?

?

 

Рис. 30. Инициализация

массива

 

 

 

Символьный массив можно инициализировать и более удоб­ ным способом:

char

kaf_name[

15 ] = "Кафедра АВТ";

Это определение символьного массива с инициализацией пол­ ностью эквивалентно предыдущему. Вместе с тем, приведенное ни­ же определение символьного массива с инициализацией дает не­ сколько иной результат:

cJiax-

kaf_name [ ] = "Кафедра АВТ"/

В последнем случае в памяти резервируется 12 байтов - ровно столько, сколько требуется для хранения инициализирующей стро­ ки.

Можно аналогичным образом определять массивы с любым типом элементов с инициализацией или без нее:

/ / Определение

массива из 9 элементов с типом double

без

//инициаЛИЗации

double

а[ 9

];

с типом double

с

// Определение

массива

из 4 элементов

//инициализацией

double b[ 4 ] = { 1.0, 2.0, -3.1, 4.5 } ;

Рассмотрим практически значимый пример, в котором исполь­ зуется массив значений с плавающей точкой. Предварительно опре­ делим понятие стек, широко применяемое в программировании и вычислительной технике.

Стек - непрерывная область оперативной памяти, в которой хранятся объекты заданного типа, и работа с которой организована по правилу "последним записан - первым прочитан". По этой при­ чине стек часто называют очередью типа LIFO (Last Input First Output).

Файл

P9.CPP

программный

проект

(файлы

Р9.СРР

и

STACK.СРР)

Двухфайловый

с тремя

функциями:

главной

функцией

и функциями

занесения

в

стек и

извлечения

из

стека.

Стек организован

на

базе

внешнего

82

статического

массива,

состоящего

из

элементов

с типом

float.

Стек доступен

в

функциях

не

за счет

передачи

и

через

список

па­

раметров^

а

за

счет

своей

области

действия

времени

жизни.

^include

<stdio.h>

 

//

Для

функций

ввода-вывода

 

//Прототипы функций

void

push

(

float

 

) ;

int

 

& ) /

 

 

 

 

 

 

 

 

 

 

 

void,

pop

(

float

 

&r

 

 

 

 

 

 

 

 

 

 

 

 

int

 

main

(

void

 

)

 

 

 

//

Возвращает

0

при

успехе

 

{

 

int

 

 

 

 

flag;

 

 

//

0

-

извлечение

 

из

стека

не

 

 

 

 

 

 

 

 

 

 

 

 

 

float

 

 

 

 

 

 

 

 

//

выполнено

г

иначе

-

выполнено

 

 

 

 

 

 

out_value;//

 

Значение,

полученное

из

стека

 

 

push

(

2.4f

 

)

;

 

 

//

Занести

в

стек

2.4f

 

 

 

 

 

push

(

-17.

4f

) ;

 

//

Занести

в

стек

 

-17.4f

 

 

 

 

 

for(

int

 

1

= 0;

i

< 3;

 

±++

)

 

 

 

 

 

 

 

 

 

 

 

{

//

 

Извлечение

 

из

стека

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

;

 

 

 

 

 

 

 

 

 

 

 

 

pop

(

out_value,

 

flag

)

 

 

 

 

 

 

 

 

 

 

 

 

//

 

Анализ

полученного

 

результата

 

 

 

 

 

 

 

 

 

if(

 

flag

 

)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{

 

printf(

 

"\n

Результат

 

извлечения:

 

%f

",

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

out_value

) ;

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

else

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{

 

printf(

 

"\n

Стек пуст"

) ;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

 

return

 

0;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Файл

STACK.CPP

для

занесения

 

элемента

и

извлечения

эле­

 

Содержит

функции

 

мента из

 

стека.

 

Используется

в

программном

проекте,

 

главная

функция

которого

 

имеется

в

файле

Р9.СРР

 

 

 

 

 

 

V

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

^include

<stdio.h>

 

 

 

//

Для

функций

 

 

ввода-вывода

 

//

Прототипы

функций

 

принципе

 

-

прототипы

здесь

 

не

нужны,

//

 

мы

оставляем

их

в

этом

файле

только

для

унификации

void

push

(

float

 

) ;

int

 

& )

;

 

 

 

 

 

 

 

 

 

 

 

void

pop

(

float

 

 

&,

 

 

 

 

 

 

 

 

 

 

 

 

^define

N10

 

 

 

 

 

 

//

Размер

стека

 

 

 

 

 

 

//

Стек:

массив

из

10

элементов

 

- доступен

 

только

в

этом

83

//

файле,

время

жизни

-

программа

 

 

 

static

float

s[

N

] ;

 

 

 

 

 

 

//

Указатель

вершины

стека:

вначале

стек

пуст

static

unsigned,

 

int

 

 

 

 

 

 

 

 

 

 

 

 

top;

 

 

 

 

 

 

 

 

//

Занесение

в

стек

 

 

 

 

 

 

 

 

void

 

push(

V

)

 

//

Заносимое

значение

 

 

float

 

 

{

if(

top

< N )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{

s[

top++

]

= V/

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

else

 

 

 

 

 

 

 

 

 

 

 

 

{

print

f (

"\n

Стек

полон

- занесение

не

выполнено" ) ;

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

return;

 

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

//

Извлечение

из

стека

 

 

 

 

 

 

void

pop (

 

 

 

значение

-

передается

по

ссылке

 

//

Извлеченное

 

 

float

 

&out,

не

выполнено

(стек

пуст)

 

//

О - извлечение

 

 

int

 

&f

)

 

 

 

 

 

 

 

 

{

if(

top

> О )

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

{

out

= s[

 

--top

];

f

=

1;

 

 

 

 

 

 

 

 

 

}

else

(

f = 0;

}

return;

Результаты выполнения данной программы имеют вид:

Результат извлечения: -17.400000 Результат извлечения: 2.400000 Стек пуст

Рассмотренные выше примеры использовали так называемые одномерные массивы, обращение к элементам которых выполняется с помощью одного индекса (индексного выражения). Указанное ин­ дексное выражение (например, /+/) должно иметь целый тип без

84

знака, так как индексы элементов массива начинаются со значения индекса, равного нулю.

Наряду с такими массивами можно использовать двухмерные массивы и массивы большей размерности. Рассмотрим еще один ил­ люстрирующий пример.

 

Файл

Р10.СРР

 

 

проект с

одной

главной

 

функцией.

 

Однофайловый

программный

с

из

Пример

иллюстрирует

работу

двумерными

массивами

элемен­

тов символьного

типа

 

 

 

 

 

 

 

 

 

V

 

 

 

 

 

 

 

 

 

 

 

 

^include

<stdlo.h>

//

Для функций

ввода-вывода

 

^define

N 8

 

//

Строковый

размер

массива

 

^define

М 16

 

//

Столбцовый

размер

 

массива

 

//

Определение

двухмерного

символьного

 

массива

с

символов

//

инициализацией:

N строк,

каждая

строка

из

М

char

 

arr__kaf_name

[

N ] [ М] =

 

 

 

 

 

{

"Состав ФТК:",

/ /

Инициализация

первой

строки

 

 

"1.

Кафедра

АВТ",

 

 

 

 

 

 

 

 

 

"2.

Кафедра

ТК",

 

 

 

 

 

 

 

 

 

 

"3.

Кафедра

САУ",

 

 

 

 

 

 

 

 

 

"4.

Кафедра

МУС",

 

 

 

 

 

 

 

 

"3 . Кафедра ИИТ", "6. Кафедра САПР",

"7 . Кафедра СУД"

} ;

int main

( void

)

//

Возвращает

О при

успехе

 

{

 

 

 

 

 

 

 

 

a u to

±nt

Index;

//

Индекс строки

двухмерного

массива

fori

Index

= 0;

Index

< N; lndex++

)

 

 

 

{

 

 

 

 

 

 

 

 

print f

( "\n

%s "r

arr_kaf_name

[

Index

] ) .

 

}

 

 

 

 

 

 

 

 

return

0;

 

 

 

 

 

 

 

Результаты выполнения данной программы имеют вид:

Состав ФТК:

1. Кафедра АВТ

2. Ка федра ТК

3.Кафедра САУ

4.Кафедра ИУС

5.Кафедра ИИТ

85

6,

Кафедра

САПР

7.

Кафедра

СУД

Используемый в программе двухмерный массив

arrkafname

хранит информацию, представленную в табл. 15.

 

 

 

 

 

Строки/

 

 

 

Табл . 1Ь. Структура м а с с и в а

 

 

 

 

 

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

столбцы

 

 

 

 

 

 

 

 

 

 

 

?

 

?

?

?

0

С о с т а в

 

Ф Г К \0

9

1

1

 

 

К а Ф е Д

р

а

 

А

В

т

\0

7

2

2

 

 

К а Ф е д

р

а

 

Т

к

\0

?

?

6

6

 

 

К а Ф

е д

р

а

 

С

А

п

р

\0

7

7

 

 

К а Ф е д р

а

 

с

У

Д

\0

?

В этой таблице элемент массива аггjkaf_name[ 6][ 7 ] хранит код символа 'д'. Элементы этого массива в оперативной памяти ком­ пьютера располагаются в подряд идущих байтах по строкам:

arr_kaf_name

[

О

][

 

О

]

arr_kaf__name[

О

][

arr_kaf_name

[

О

] [

15

J

 

 

 

 

arr_kaf_name

[

1

][

 

О

]

агг kaf namef

1

] [

агг kaf_name[

 

1

][

15

]

 

 

 

 

arr_kaf__name

[

7

] [

О

]

arr_kaf_name

[ 7

] [

arr_kaf__name

[

7

][

15

 

 

 

 

 

3.9.2. Массивы - как аргументы функций

Массив, в отличие от других видов данных, рассмотренных нами, всегда передается в функцию по ссылке, а не по значению. По­ чему?

Потому, что массив может занимать много места в памяти и копировать его, как это делается при передаче аргумента по значе­ нию, расточительно.

Так как массив занимает смежные ячейки памяти, то при ис­ пользовании имени массива в качестве аргумента языки Си/Сн-+ обеспечивают передачу функции адреса первого элемента этого массива. Модифицируем последний пример.

Файл

Р11,СРР

 

 

 

 

 

 

 

Одно файловый

программный

проект

с двумя

функциями.

Пример

иллюстрирует

передачу

массива

в

 

функцию

 

 

^include

<stdlo.h>

//

Для

функций

ввода-вывода

 

86

^define

N8

 

 

 

//

Строковый размер

массива

idefine

М 16

 

 

 

//

Столбцовый

размер

массива

//

Определение

 

двухмерного

символьного

массива

с

символов

//

 

инициализацией:

N строк^

каждая

строка из М

static

char

 

arr_kaf_name

[

N ] [ М] =

 

 

 

{

 

"Состав

ФТК:",

 

 

//

Инициализация

первой

строки

 

 

 

 

 

 

"1 .

Кафедра

АВТ",

 

 

 

 

 

 

 

 

 

 

"2.

Кафедра

ТК",

 

 

 

 

 

 

 

 

 

 

"3,

Кафедра

САУ",

 

 

 

 

 

 

 

 

 

 

"4.

Кафедра

МУС",

 

 

 

 

 

 

 

 

 

 

"5.

Кафедра

ИИТ",

 

 

 

 

 

 

 

 

 

 

"6.

Кафедра

САПР",

 

 

 

 

 

 

 

 

) ;

 

"7.

Кафедра

СУД"

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

//

Прототип

функции:

обратите

внимание

как

записывается

//

 

параметр

-

двумерный

массив.

Здесь

прототип

также не

//

 

обязателен,

так как файл

содержит определение

функции

void

display

 

( cha.r

arr_kaf_name

[

N] [

М ] ) ;

 

 

int

 

main (

void

)

 

//

Возвращает

0

при

успехе

{

 

//

Вызов

функции,

печатающей

строки

массива:

обратите

 

 

 

 

//

внимание,

как

записывается

 

аргумент-массив

 

 

display

(

arr__kaf_name

) ;

 

 

 

 

 

 

 

 

return

0;

 

 

 

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

//

Печать

строк

двухмерного

 

массива

 

 

 

 

void

display

 

(

 

 

 

передается

по

ссылке,

т.е. не

 

 

//

Массив

для печати:

//копируется

char

 

arr kaf

name [

N] [ М ] )

 

 

{

 

 

 

 

 

 

 

 

auto

int

index;

 

 

//

Индекс строки двухмерного

массива

fori

index

= О;

index

< N; index++

)

 

{

printf(

"\n

%s

",

arr_kaf_name[

index ] ) ;

 

 

 

}

return;

}

Результаты выполнения этой программы выглядят так же, как

иу предыдущей программы.

Вдополнение к сказанному выше приведем пример инициали­ зации двумерного массива с типом, отличным от символьного типа:

87

// Определение

внешнего

статического

массива

с

 

типа:

//

инициализацией.

 

Массив

содержит

элементы

целого

//

N строк,

каждая

строка

из М элементов

 

 

 

 

stable

±zit

 

а[

3

] [

4 ]

=

 

 

 

 

 

{

1

, 3 ,

5 ,

1 }

,

 

//

Инициализация

первой

строки

{

 

{

2,

4,

6,

8

} ,

 

 

 

 

 

 

 

 

{

3,

5,

7,

9

}

 

 

 

 

 

 

 

 

} ;

Резюмируя сказанное, можно заключить, что массив представ­ ляет собой объект, состоящий из некоторого количества взаимосвя­ занных однотипных элементов.

В противоположность массиву, может потребоваться объект, состоящий из некоторого количества взаимосвязанных разнотипных элементов. Такое данное называют в языке Си структурой. Сразу же отметим, что объект с типом "структура" в языке C++ имеет бо­ лее широкий смысл, чем в языке Си. Ниже мы вначале рассмотрим структуру с точки зрения языка Си.

3.9.3. Упражнения для самопроверки

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

вычислить сумму элементов одномерного массива х[ 7V ] {N ~ 50) це­

 

лого типа имеющих нечетные индексы;

получить одномерный массив z[N]

(N = 40) из двух заданных масси­

 

вов целого типа jc[ Л^ ], >'[ Л^ ] по правилу:

 

z[ i ] := тах{ х[

1 ], у[ i ] }

Возможный вариант ответа можно посмотреть в разд. 18.

3.9.4. Структуры

Различают объявление структуры и определение структурного объекта. Объявление структуры и определение структурного объек­ та можно выполнять в Си-программе по отдельности или же совме­ стно. Поясним сказанное примерами.

/*

Объявление

структуры,

содержащей

сведения

о студенте.

Об­

ратите

внимание,

что

данное

объявление

не

размещает

никакого

объекта

в оперативной

памяти,

а лишь

вводит

новый

структурный

тип

 

 

 

 

 

 

 

 

 

3timet

STUDENT__INFO

 

 

 

 

 

 

 

88

//Фа культет

char

 

fak_name[

 

30

];

 

 

char

 

fio[

30

];//

ФИО

 

 

//

Номер

группы

 

 

 

 

 

 

char

 

group_name[

 

7

];

 

 

char

 

date[

9

];//

Дата

поступления

студента в ВУЗ

float

stip/

 

 

//

Размер

стипендии

 

};

 

 

 

 

 

 

 

 

Здесь использованы следующие соглашения:

 

struct

- начало

объявления

 

и/или

определения;

 

STUDENT_INFO -

имя

(тэг)

 

структуры;

 

{

- список

 

 

 

 

 

 

 

...

элементов

(полей)

структуры

 

} ;

 

 

 

 

 

 

 

 

Обратите внимание, что, тэг структуры принято записывать с использованием прописных букв. Элементы структуры могут иметь любой тип, допустимый в языке Си, например, тип массива, струк­ турный и т.п. Как уже указывалось, приведенное объявление создает

новый тип с именем

STUDENTINFO.

 

 

 

 

 

Для создания же структурированного объекта надо использо­

вать его

определение:

 

 

 

 

 

 

 

 

//

Данное

определение

размещает

объект

current

в

оперативной

//

памяти

компьютера

и

использует

 

ранее

сделанное

 

//

объявление

типа

STDENT_INFO

 

 

 

 

 

struct

STUDENT_INFO

 

 

 

 

 

 

 

 

 

 

 

current;

 

//

Для

Си

или

C++

 

 

 

 

 

 

 

 

ИЛИ

 

 

 

 

/ /

Только

для

C+ + , если

нет

объекта

с

другим

типом

и именем

//

STUDENT__INFO

 

 

 

 

 

 

 

 

STUDENT_INFO

current;

 

 

 

 

 

 

 

Приведенное выше объявление структуры STUDENT INFO и определение структурированного объекта current можно объеди­ нить, причем это даст такой же результат:

/'^

Комбинация

объявления

структурного

типа

и

определения

структурного

объекта

 

 

 

 

 

V

 

 

 

 

 

 

 

struct

STUDENT_INFO

// Тэг

структуры

 

 

 

{

//Факультет

char fak name[ 30 ];

89