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

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

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

int main

( void.

)

 

//

Возвращает

0

при

успехе

{

±nt

i

=

О; i

< 2;

i + ч- )

 

 

 

 

£оз:(

 

 

 

 

{

pr±ntf(

 

"\n%d

%d %d"r

a[i][2-i],

 

 

*a[i],

 

 

 

 

 

 

 

 

*(*(a

+ i)+±)

)

;

 

 

 

 

jretux-22

0;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

d)

 

 

 

 

 

 

 

 

 

срр 'W

 

 

 

 

 

 

 

 

ср 1 ^

 

 

 

 

 

 

 

 

 

с

н^^^^

 

 

 

 

 

 

 

 

/

 

 

 

 

 

 

 

 

 

 

/ (

<

|

\

\

 

 

 

 

" pl гт~

 

Е

 

N

Р

г_

 

 

 

 

 

 

F

 

 

 

 

" Q |

гг

 

N

 

Е

О

1

 

 

 

 

"Tl ГЦ

 

Т

 

Р

1

R

 

 

 

\0

"NI ГЦ

 

Е

 

\0

N

S

 

 

 

 

T l

ГТ"

 

R

 

 

Т

Т

 

 

\0

1

\о 1 \о

1

( *( срр[ -2 ] ) ) + 3

( ( срр[ -1 ] )

[ -1 ]) +

1

I

I

1

1

1

 

 

 

 

 

 

1

 

 

 

 

2

 

г.

 

 

 

3

1

 

 

 

 

 

 

 

Приоритет [ ] выше,

 

 

 

чем *.

 

3

 

ого

 

Будет напечатано с

Операци

одинаков

 

 

 

приоритета, выпол­

 

учетом предыдущей

няются справа налево.

печати:

Будет напечатано с

POINTER ST

учетом предыдущей

 

 

печати:

 

РOIN"ГЕК STEF3

Прод. рис. 43

Так как a[i] означает адрес первого элемента строки / массива "(з", то *л[/] есть значение этого элемента. Аналогично, a+i эквива­ лентно &а[/], *{a+i) эквивалентно a[i], *(a-^i)+i эквивалентно a[i]-^i и

140

эквивалентно &а[/][/]. Следовательно, *(*(а+/)-н/) эквивалентно a[im.

Таким образом, программа напечатает:

3 11

5 4 5

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

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

^include <stdio.h>

±nt main ( void

)

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

int

 

 

 

a[

]

= { 10,

11, 12,

13,

14, 15, 16 };

±nt

 

 

 

i,

*p;

 

 

 

fo2:(

p

 

= a,

i = 0; p

+ 2*1 <= a + 6; p+ +, i++ )

 

prlntf(

 

"

%3d",

*( p + 2*1 )

);

printf

(

"\n"

) ;

 

 

 

fojci

p

 

= a + 5; p >= a + 1; p

-= 2 )

 

printf

(

"

%3d",

*p ) ;

 

 

printf

(

"\л"

; /

 

 

 

return

 

0;

 

 

 

 

 

 

2.

Что будет напечатано?

 

 

 

 

 

 

^include

 

<stdio.h>

 

 

 

 

 

 

 

 

int main ( voxd )

 

 

 

 

 

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

int

 

 

a[ ]

= {

10,

11,

12,

13,

14

};

 

int

 

 

*p[]

= { a ,

a + 1,

a + 2 ,

a

+ 3, a-i-4},

int

 

 

**pp

= p/

 

 

 

 

 

 

pp

= pp

+ 4;

 

 

 

 

 

 

 

 

 

printf

(

"pp-p

=%3d *pp-a

=%3d

**pp =%3d\n",

pp-p,

 

 

 

*pp-a,

**pp ) ;

 

 

 

 

 

 

*pp-~;

 

 

 

 

 

 

 

 

 

 

 

printf

(

"pp-p

=%3d *pp-a

=%3d

**pp =%3d\n",

pp-p,

 

 

 

*pp-a,

**pp ) ;

 

 

 

 

 

 

*++pp;

printf( "pp-p ==%3d *pp-a =%3d **pp =%3d\n", pp-p, *pp-a, **pp );

141

--^рр;

"рр-р

=%3d

*рр-а =%3d **рр =%3d\n", рр-р,

printf(

 

*рр-а,

**рр

) ;

jcetuim О;

)

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

7. ПОЛЯ БИТОВ И ПОБИТОВЫЕ ОПЕРАЦИИ

7.1. Поля битов

в отличие от других языков высокого уровня в языке C++, как и в ассемблерных языках, имеется развитый набор средств манипу­ лирования битами. На рис. 44 показано представление символа эк­ рана в видеопамяти:

7

6

5

4

3

Номера битов в байтах

 

 

 

 

 

 

2

1

0

7

6

5

4

3

2

1

 

0

 

Цвет фона

 

Цвет

 

 

 

Код символа

 

 

 

 

символа

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

15

14

13

12

11

10

9

8

7

6

5

 

4

3

2

1

О

 

 

 

 

 

Номера битов в слове

 

 

 

 

 

 

 

 

Старший байт

 

 

 

 

Младший байт

 

 

 

 

 

 

 

 

 

 

Интенсивность символа

 

 

 

 

 

 

 

 

 

 

Признак мерцания

 

 

 

 

Рис. 44. Представление символа экрана в видеопамяти

Поля битов объявляются как элементы структуры по правилу:

Спецификация_типа идентификатор: размер поля

В качестве "спецификации типа" задается обычно unsigned int (для шестнадцатиили тридцатидвухразрядного процессора слово из 16 или 32 битов), а размер поля - целая константа в диапазоне от О до 16 или 32. Ниже будет рассматриваться случай с 16-разрядным процессором.

/ /

Представление

слова

видеопамяти^

представляющего

символ на

//

 

экране, в

виде

структуры

с битовыми

полями

 

stxract

WORD

 

 

 

 

 

 

 

 

 

{

unsigned

int

blink:

1;

//

Мерцание

 

 

 

 

unsigned,

int

bkgrd:

3;

//

Цвет

 

фона

 

 

unsigned

int

in

tens:

1;

//

Интенсивность

символа

 

unsigned

int

forgrd:

3;

//

Цвет

 

символа

 

 

unsigned

int

ch

 

: 8;

//

Код

 

символа

 

}

sd;

 

 

 

 

 

//

Symbol

Display

 

В структуре поля битов можно смешивать с другими элемен­ тами, не являющимися полями битов. Если это происходит, то пер-

143

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

/ / Доступ

к отдельным

полям

битов в структуре

и

присваивание

//им значений

sd.blink

 

= 1;

 

//

Установить

мерцание

// Установить

цвет

символа

- три

единичных

бита

sd,forgrd

 

=

1;

 

 

 

 

 

sd.ch

=

'А';

 

//

Буква

«А»

прописная

WORD

 

 

*psd

= &sd;

 

 

 

 

psd->bkgrd

=

3;

//

Установить

цвет

фона

Допускаются два специальных объявления поля битов. Можно объявлять безымянные поля для того, чтобы следующее поле заняло заданные биты слова:

/

/ Использование

 

безымянных

 

полей

битов

 

stiracb

CONTROL

 

 

 

 

 

{

unsigned

int

flagl:

 

1;

 

 

 

 

 

 

 

unsigned,

int

s__s

:

4;

 

 

 

 

 

 

 

:

2;

// Два неиспользуемых

бита

 

unsigned

int

flag2:

 

1;

 

 

}

;

 

 

 

 

 

 

 

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

И еще повторно сделаем важное замечание. В языках Си/С+-1- не допускаются указатели на поля битов и на массивы полей битов.

7.2. Побитовые операции

Побитовые операции можно применять только к объектам це­ лого и символьного типа. С их помощью можно проверять и моди­ фицировать биты в данных целого и символьного типа. Побитовые операции перечислены в табл. 21.

Операции Иу ИЛИ, исключающее ИЛИ. Эти операции дейст­ вуют на каждый бит соответствующего операнда (операндов) так, как это показано в табл. 22. При этом если операнды имеют различ-

144

ные типы, то они перед выполнением операции приводятся к одина­ ковому (старшему) типу по правилам, аналогичным указанным в подразд. 5.3. С помощью операции "&" удобно проверять и обну­ лять биты, операции "|" - устанавливать биты, а операции "^" - про­ верять несовпадение битов.

Табл. 21. Побитовые операции

&-

Операция

Назначение

 

бинарная

И

 

1

- бинарная

ИЛИ

j

А

- бинарная

Исключающее ИЛИ

 

 

 

 

«

- бинарная

Сдвиг влево

 

»

- бинарная

Сдвиг вправо

 

~

- унарная

Дополнение до единицы

 

Табл. 22. Определение операций " & " , "|", ' Л "

Бит левого

Бит правого

Ы&Ьг

Ы\Ьг

Ы" Ьг

 

операнда (Ь/)

операнда (Ьг)

0

0

0

 

0

0

 

1

0

0

1

1

 

0

1

0

1

1

 

1

1

1

1

0

i

Операция

\ :

Операция

^:

Операция

&:

0001001101100011

 

0001001101100011

 

0001001101100011

 

0100001100100001

 

0100001100100001

 

0000000000000001

 

0101001101100011

 

0101000001000010

 

0000000000000001

 

Операция сдвига влево. Формат операции:

операнд « выражение

В результате биты "операнда" будут сдвинуты влево на число битов, задаваемое значением "выражения". Освобождающиеся спра­ ва биты заполняются нулями. Допустимые значения "выражения" изменяются в диапазоне от О до 8*5'/zeoy( операнд ).

/*

Файл

РЗЗ. СРР

 

 

 

 

 

 

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

программный

проект

с

одной

функцие

й. Пример

иляюстрируе

т

действие

операции

сдвига

влево

(ВС+ + 3.

1)

V

 

 

 

 

 

 

 

 

iinclude

<stdlo.h>

//

Для функций

 

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

±nt main

( void.

)

//

Возвращает

 

О при

успехе

 

{

 

 

 

 

 

 

 

 

±nt

 

к == 1;

 

 

 

 

 

 

145

print

f( "\n%dr

%d,

%d,

%d,

%d,

%d,

%d, %d", x « I ,

x«2,

 

x«3,

x«0,

x«30r

х«-327б8,

x«-32161,

 

 

x«-32766

) ;

 

 

 

 

 

теturn

G;

 

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

// Будет

напечатано

2,

4,

8, 1,

О,

1,

2, 4

 

Нетрудно заметить, что сдвиг битов "операнда" влево на одну позицию эквивалентен умножению на два. Заметим также, что отри­ цательные значения "выражения" или значения, равные или превы­ шающие число битов в операнде, в общем случае недопустимы и дают неопределенное значение, зависящее от реализации. Приве­ денный выше пример иллюстрирует особенности реализации языка Borland C++ версии 3.1 для подобной ситуации.

Операция сдвига вправо. Формат операции:

операнд » выражение

В результате биты "операнда" будут сдвинуты вправо на число битов, задаваемое значением "выражения". Допустимые значения "выражения" изменяются в диапазоне от О до S'^sizeofl операнд ). Операция сдвига вправо выполняется аналогично сдвигу влево, но отличие состоит в способе заполнения освобождающихся битов:

если "операнд" беззнаковый, то освобождающиеся биты запол­ няются нулями;

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

Файл

Р34.СРР

 

 

проект

с

одной

функцией.

Пример

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

программный

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

действие

операции

сдвига

вправо

(ВС++

3.1)

^include

<stdio.h>

 

//

Для

функций

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

 

int main

( void

)

 

//

Возвращает

О при

успехе

 

{

 

 

 

 

 

 

 

 

 

 

 

int

 

 

X

= 64;

 

 

 

 

 

 

 

printf(

 

"\n%d,

%d,

%dr %d,

%dr %dr

%d, %d"r x » l ,

x»2,

 

 

x»3,

 

x»Or

x»30r

 

x»-32768r

 

x»-32767,

 

 

 

x>>-32 766 ) ;

 

 

 

 

 

 

return

 

0;

 

 

 

 

 

 

 

 

 

146

}

// Будет напечатано

32, 16, 8, 64, О, 64, 32, 16

Аналогично предыдущему случаю заметим, что сдвиг битов "операнда" вправо на одну позицию эквивалентен делению на два. Обратите внимание также, что отрицательные значения "выраже­ ния" или значения, равные или превышающие число битов в опе­ ранде, в общем случае недопустимы и дают неопределенное значе­ ние, зависящее от реализации. Приведенный выше пример показы­ вает особенности реализации языка Borland С+-ь версии 3.1 для по­ добной ситуации.

Дополнение до единицы. Операция изменяет значения всех битов операнда на противоположные значения:

 

 

 

 

 

 

-выражение

 

 

//

Рассмотрим

пример

 

 

 

char

с

&

'OxlF';

с,

d;

Обнулить

(маскировать)

старший

с

=

 

//

с

=

с

&

(- ' 0x7F) ';

/ /

бит

все биты,

кроме

//

Маскировать

 

 

 

 

 

 

//

старшего

 

Операции присваивания. Если бинарные побитовые операции используются в операторах присваивания вида

value = value побитовая_операция

выражение

то можно использовать сокращенную запись (табл. 23):

 

 

value побитовая_операция=

выражение

 

//

Обычная

форма

Сокраш,енная

форма

 

с = с &

'0x7F';

с &=

'0x7F'/

Приоритеты побитовых операций и порядок их выполнения рассмотрены в табл. 16.

Табл. 23. Сокращенная запись побитового присваивания

Операция

Назначение

 

&=

Операция "И" и присваивание

 

1=

Операция "ИЛИ" и присваивание

 

Л=:

Операция "^" и присваивание

i

~=

Дополнение до единицы и присваивание

 

« =

Сдвиг влево и присваивание

 

» =

Сдвиг вправо и присваивание

 

8. ДИНАМИЧЕСКОЕ РАЗМЕЩЕНИЕ ОБЪЕКТОВ В ПАМЯТИ.

ОДНОНАПРАВЛЕННЫЙ НЕКОЛЬЦЕВОЙ ЛИНЕЙНЫЙ СПИСОК И ОПЕРАЦИИ С НИМ

8.1. Понятие об однонаправленном линейном списке. Динамическое размещение объектов в памяти

Сущность однонаправленного линейного списка (ЛС), предна­ значенного для хранения символов, представлена на рис. 45.

Т'

'е'

'к'

'с'

'т'

W

W

W

W

NULL

I start

Рис. 45. Однонаправленный линейный список

Каждый элемент ЛС содержит две части (два поля):

основное поле, в котором хранится содержательная информация (в нашем примере - символ);

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

Список содержит два элемента, которые являются особенны­ ми, отличными от других. Это первый (головной) элемент ЛС - его особенность состоит в том, что он снабжен указателем (на рис. 45 таким указателем является Start), Без этого указателя нельзя рабо­ тать с линейным списком. Последний элемент ЛС также является особенным, так как в его вспомогательном поле хранится указатель NULL, означающий, что следующего элемента нет.

Из сказанного следует, что элемент ЛС в терминах языка С++ можно определить в виде структуры:

stxract

ELЕМ

 

 

 

 

 

 

{

char

ch;

//

Основное

поле:

символ

 

 

 

 

ELEM

*neKt;

//

Указатель

на

следующий

элемент

} ;

 

 

 

 

 

 

 

ELEM

 

*pe;

//

Указатель

на

структуру

 

148

в языках Си/С++ имеется возможность динамического разме­ щения некоторого объекта в оперативной памяти (функция malloc{ ) и др. в библиотеке языка Си, оператор new языка СН-+) или освобо­ ждения занятой ранее динамической памяти (функция free{ ) в биб­ лиотеке языка Си, оператор delete языка C++):

 

Пример

размещения

Си

элемента

линейного

 

списка

в

 

динамической

памяти.

Среда

языка

 

 

 

 

 

 

 

 

 

 

 

 

 

^include

 

<malloc

.h>

 

/*

 

Для функции

 

та Нос

 

 

 

*/

^include

 

<stdio.h>

 

/*

 

Для

функций

 

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

 

 

*/

^include

 

<stdlib.h>

 

/'*'

Для

функции

 

exit

 

 

 

 

*/

ре

= (

struct

 

ELEM * ) malloc

(

sizGof

(

struct

ELEM )

) ;

 

±f(

pe

== NULL

)

 

 

/*

 

Обработка

результата

 

размещения"^/

{

printf

(

"\n

Размещение

 

элемента

в

динамической

 

памяти

не"

 

 

 

 

 

 

 

"

выполнено

"

) ;

 

 

 

 

 

 

 

 

 

 

 

exit(

1

) ;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

pe->ch . . .

 

 

 

/'*'

 

Значение

поля

данных

структуры

*/

. . .

 

 

 

 

. . . pe->next

. . .

 

 

/*

 

Указатель

на

следующий

элемент

'^/

 

 

 

 

 

 

 

 

/*

 

 

линейного

 

списка

 

 

 

*/

 

Пример

размещения

элемента

 

линейного

 

списка

в

 

динамической

памяти.

Среда

языка

C++

 

 

 

 

 

 

 

 

 

 

 

 

*/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

^include

 

<stdio.h>

 

 

//

 

Для

функций

 

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

 

#include

 

<stdlib.h>

 

//

 

Для

функции

 

exit

 

 

 

 

 

ре

= new ELEM;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

±f(

ре

== NULL

)

 

 

//

 

Обработка

результата

 

размещения

{

printf

(

"\п

Размещение

 

элемента

в

динамической

 

памяти

"

 

 

 

 

exit

(

 

"не

выполнено

" )

;

 

 

 

 

 

 

 

 

 

 

1 ) /

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

}

pe->ch . . .

 

 

 

/ /

 

Значение

поля

данных

 

структуры

 

. . .

 

 

 

 

 

 

. . .

pe->next

. . .

 

 

/ /

 

Указатель

на

следующий

 

элемент

 

 

 

 

 

 

 

 

 

//

 

 

линейного

 

списка

 

 

 

 

/*

Пример

освобождения

динамической

памяти^

занятой

элементом

 

линейного

списка.

Среда

языка

Си

 

 

 

 

 

 

 

 

" /

finclude

<malloc.h>

/* Для функции free

*/

149