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

книги / Проектирование программ и программирование на C++. Структурное программирование

.pdf
Скачиваний:
3
Добавлен:
12.11.2023
Размер:
3.86 Mб
Скачать

Тип NK чет быть любым, кроме ссылки.

Размер указателя зависит от модели памяти. Можно определить

указатель на указатель i n t * * а ;

Указатель может быть константой или переменной, а также ука­

зывать на константу или переменную.

 

 

 

 

i n t i ;

 

//ц е л а я

п ерем ен ная

 

c o n s t

i n t c i = l ; //ц е л а я к о н ст а н т а

 

i n t *

p i ;

 

//у к а з а т е л ь

на

целую

переменную

c o n s t

i n t *

p c i ; //у к а з а т е л ь

на

целую

к о н ста н ту

Указатель можно сразу проинициализировать:

 

//у к а з а т е л ь

на

целую

переменную

 

i n t *

p i= & i;

 

 

 

 

 

 

 

//у к а з а т е л ь

на

целую

к о н ста н ту

 

c o n s t i n t * p c i= & c i;

 

 

 

 

 

//у к а з а т е л ь - к о н с т а н т а

на

переменную

целого типа

i n t * c o n s t c p i= & i;

 

 

 

 

 

//у к а з а т е л ь - к о н с т а н т а

на

целую к о н ст а н т у

c o n s t

i n t *

c o n s t cp c= & ci;

 

 

 

Если модификатор c o n s t относится к указателю (т.е. находится

между именем указателя и *), то он запрещает изменение указателя, а если он находится слева от типа (т.е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

Для инициализации указателя существуют следующие способы:

с помощью операции получения адреса

i n t а = 5 ;

 

i n t *

р=&а; или i n t

р (& а );

с помощью проинициализированного указателя

in t *

г= р;

 

адрес присваивается в явном виде

c h a r*

c p = (c h a r* )0 x

В800 0000;

где Ox

В800 0000 - шестнадцатеричная константа; (c h a r* ) -

операция приведения типа.

 

присваивание пустого значения:

i n t *

N=NULL;

 

i n t *

R=0;

 

13.2. Динамическая память

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

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

Для создания динамических переменных используют операцию new, определенную в C++:

указатель = new имя_типа[инициализатор];

где инициализатор - выражение в круглых скобках.

Операция new позволяет выделить и сделать доступным участок динамической памяти, который соответствует заданному типу дан­ ных. Если задан инициализатор, то в этот участок будет занесено значение, указанное в инициализаторе.

int* x=new int(5);

Для удаления динамических переменных используется операция delete, определенная в C++:

delete указатель;

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

delete х;

13.3.Операции с указателями

Суказателями можно выполнять следующие операции: - разыменование (*); - присваивание;

-арифметические операции (сложение с константой, вычита­ ние, инкремент ++, декремент — );

-сравнение;

-приведение типов.

Операция разыменования предназначена для получения значе­ ния переменной или константы, адрес которой хранится в указателе. Если указатель указывает на переменную, то это значение можно изменять, также используя операцию разыменования.

i n t а ;//п е р е м е н н а я

ти па i n t

i n t *

pa=new

i n t ; / /у к а з а т е л ь и вы деление

//п а м я т и

под динам ическую переменную

* р а = 1 0 ;//п р и с в о и л и

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

//п е р е м е н н о й ,

на которую у к а зы в а е т у к а з а т е л ь

а = * р а ;//п р и с в о и л и

зн ач ен и е перем енной а

Присваивать значение указателям-константам запрещено. Приведение типов. На одну и ту же область памяти могут ссы­

латься указатели разного типа. Если применить к ним операцию ра­ зыменования, то получатся разные результаты,

i n t а= 1 2 3 ;

i n t *

pi= & a;

c h a r*

р с = ( c h a r * ) &а;

f l o a t * p f = ( f l o a t * ) & a ;

p r i n t f ( " \n % x \t% i" , p i , * p i ); p r i n t f ( " \ n % x \ t % c " ,p c ,* p c ) ; p r i n t f ( " \ n % x \ t % f " , p f , * p f ) ;

При выполнении этой программы получатся следующие результаты:

6 6 fd 9 c

123

6 6 fd 9 c

{

6 6 fd 9 c

0 .0 0 0 0 0 0

Таким образом, адрес у трех указателей один и тот же, но при разыменовании получаются разные значения в зависимости от типа указателя.

В примере при инициализации указателя была использована опе­ рация приведения типов. При использовании в выражении указателей разных типов явное преобразование требуется для всех типов, кроме v o id * . Указатель может неявно преобразовываться в значения типа b o o l, при этом ненулевой указатель преобразуется в t r u e , а нуле­ вой в f a l s e .

Арифметические операции применимы только к указателям од­

ного типа.

• Инкремент увеличивает значение указателя на величину

sizeof(тип). char* рс; int* pi; double* pd;

pc++;

//значение увеличится на 1

pi++;

//значение увеличится на

4

pd++;

//значение увеличится на

8

Декремент уменьшает значение указателя на величину sizeof(тип).

Разность двух указателей - это разность их значений, делен­ ная на размер типа в байтах.

int а=123/ Ь=456, с=789;

int* pil=&a;

int* pi2=&b; int* pi3=&c;

printf("\n%x",pi1-pi2); printf (,f\n%x,f,pil-pi3) ;

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

1

2

Суммирование двух указателей не допускается.

• Можно суммировать указатель и константу: pi3=pi3+2;

pi2=pi2+l;

p r i n t f ( "\ n % x \t % d ,f, p i l , * p i l ) ;

p r i n t f ( " \ n % x \ t % d " , p i 2 , * p i 2 ) ; p r i n t f ( ,,\n % x \t % d ,f, p i 3 / * p i 3 ) ;

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

66fd9c 123

66fd9c 123

66fd9c 123

14. ССЫЛКИ

Ссылка - это синоним имени объекта, указанного при инициали­ зации ссылки.

Формат объявления ссылки

тип & имя =имя_объекта;

переменной

int

х;

//

определение

int&

sx=x;

//

определение

ссылки на

//переменную х

CR='\n' //определение ссылки на

const

char&

//константу

 

 

 

Правила работы со ссылками:

-переменная-ссылка должна явно инициализироваться при ее описании, если она не является параметром функции, не описана как extern или не ссылается на поле класса;

-после инициализации ссылке не может быть присвоено другое значение;

-не существует указателей на ссылки, массивов ссылок и ссы­ лок на ссылки;

-операция над ссылкой приводит к изменению величины, на которую она ссылается.

Ссылка не занимает дополнительного пространства в памяти, она является просто другим именем объекта.

#include<iostream.h> void main()

{

int i=123; int& si=i;

cout<<"\ni="<<i<<" si="<<si;

i=456;

cout<<"\ni="<<i<<" si="<<si; i=0;

cout<<"\ni="<<i<<" si="<<si;

Выведется: i=123 si=123 i=456 si=456

i=0 si=0

При определении массива ему выделяется память. После этого имя массива воспринимается как константный указатель того типа, к которому относятся элементы массива. Исключением является ис­ пользование операции sizeof (имя_массива) и операции

&имя_ма ссива. int а [100];

/^определение количества занимаемой массивом памяти, в нашем случае это 4*100=400 байт*/

int k=sizeof(a);

/*вычисление количества элементов массива*/ int n=sizeof(a)/sizeof(а[0]);

Результатом операции &является адрес нулевого элемента массива:

имя_массива==&имя_массива=&имя_массива [0]

Имя массива является указателем-константой, значением кото­ рой служит адрес первого элемента массива, следовательно, к нему применимы все правила адресной арифметики, связанной с указате­

лями. Запись имя_массива [индекс] - это выражение с двумя

операндами: имя массива и индекс. Имя_массива - это указательконстанта, а индекс определяет смещение от начала массива. Ис­ пользуя указатели, обращение по индексу можно записать следую­ щим образом: * (имя_массива+индекс) .

for(int

i=0;i<n;i++)

//печать массива

cout«* (a+i) « " ";

 

/*к имени

(адресу) массива добавляется кон­

станта i и полученное значение разыменовывается*/

Так как имя массива является константным указателем, то его невозможно изменить, следовательно, запись * (а++) будет ошибоч­

ной, а * (а+1) - нет.

Указатели можно использовать и при определении массивов:

int

а [100]={1,2,3,4,5,6,7,8,9,10};

//поставили указатель на уже определенный массив

int*

па=а;

/*выделили в динамической памяти место под

массив из

100 элементов*/

int

b

= new int[100];

Многомерный массив - это массив, элементами которого служат массивы. Например, массив с описанием i n t а [ 4 ] [ 5 ] - это массив из четырех указателей типа i n t * , которые содержат адреса одно­ мерных массивов из пяти целых элементов (рис. 10).

 

 

 

 

 

 

*(а + 1) - адрес строки

 

 

 

 

 

 

массива с номером 1

 

0

I

2

3

4

*(*(а + 1) + 1 ) - значение

типа *int

0

I

ъ

'3

4

элемента, у которого

0

1

2

3

4

 

индексы строки и столбца

 

0

1

2

3

4

 

равны 1

Рис. 10. Выделение памяти под массив, элементами которого являются массивы

Инициализация многомерных массивов выполняется аналогично

инициализации одномерных массивов.

элементы массива

//лроинициализированы

все

int а [3][4]

= {{11,22,33,44}, {55,66,77,88},

{99,110,120,130}};

 

 

 

 

 

//лроинициализированы первые элементы каждой строки

int b [3][4]

= {{1},{2},{3}};

//лроинициализированы все

элементы массива

int с [3] [2]={ 1,2,3, 4,5, 6};

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

а [ 1 ] [ 1 ]

- доступ с помощью индексированных переменных;

* (* (а +

1 ) +1)

- доступ к этому же элементу с помощью указателей

(см. рис.

10).

 

15.3. Динамические массивы

Операция new при использовании с массивами имеет следую­ щий формат:

new тип_массива

Такая операция выделяет для размещения массива участок динамической памяти соответствующего размера, но не позволяет инициализировать элементы массива. Операция new возвращает

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

должны быть полностью определены.

 

 

//в ы д е л е н и е

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

пам яти

 

1 0 0 * s i z e o f ( i n t ) б ай т

 

 

 

i n t *

а =

new

i n

t [1 0 0 ];

 

 

//в ы д е л е н и е

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

пам яти

 

1 0 * s i z e o f ( d o u b l e ) б ай т

 

 

 

d o u b le *

b =

 

new

d o u b l e [1 0 ] ;

 

/ /у к а з а т е л ь

 

на

м асси в

и з

4 эл ем ен то в

ти па lo n g

l o n g (* 1 а ) [ 4 ] ;

 

 

 

 

//в ы д е л е н и е

 

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

пам яти

 

2 * 4 * s i z e o f ( l o n g ) б ай т

 

 

 

l a = n e w [ 2 ] [ 4 ] ;

 

 

 

 

/ /д р у г о й

сп о со б

вы делени я

п ам яти под

двумерный

//м а с с и в

 

 

 

 

 

 

 

 

in t * *

m a tr

=

( in t* * )

new

i n t [ 5 ] [ 1 0 ] ;

 

Изменять значение указателя на динамический массив надо ак­ куратно, так как этот указатель затем используется при освобожде­

нии памяти с помощью операции d e l e t e .

/ * о св о б о ж д ает п а м я т ь , выделенную под м а сси в ,

е с л и а а д р е с у е т е г о н а ч а л о * /

d e l e t e []

а ;

d e l e t e []

b ;

d e l e t e []

l a ;

Если квадратных скобок нет, то сообщение об ошибке выдавать­

ся не будет, но помечен как свободный будет только первый элемент массива и остальные ячейки окажутся недоступными для дальнейше­ го использования («мусор»).

//У д а л и т ь и з матрицы с т р о к у с номером К

#i n c l u d e < io s tr e a m .h >

#i n c l u d e < s t r i n g . h >

#i n c l u d e < io s tr e a m .h >

# i n c l u d e < s t d l i b . h>

 

v o id m a in ()

 

{

 

 

i n t

n ,m ;

//р а з м е р н о с т ь матрицы

i n t

i , j ;

 

cout«"\nEnter n";

количества

строк

cin»n;

//ввод

cout«"\nEnter m";

количества

столбцов

cin»m;

//ввод

//выделение памяти под массив указателей на

//строки

int* [n];

 

int** matr = new

 

//выделение памяти под элементы матрицы

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

 

 

 

matr[i] = new int [m];

 

//заполнение матрицы

 

 

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

 

 

 

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

= rand()%10);

 

matr[i][j]

 

//печать сформированной матрицы for(i=0;i<n;i++)

{

for(j=0;j<m;j++) cout«matr[i] [j]«,f

cout«"\n";//переход на следующую строку

}

//удаление строки с номером к int к;

cout«M\nEnter к"; cin»k;

//формирование новой матрицы int** temp = new int*[n-l]; for(i=0;i<n;i++)

temp[i]=new int [m] ;

//заполнение новой матрицы int t; for(i=0,t=0;i<n;i++)

/*если номер строки отличен от к, переписываем строку в новую матрицу*/

if(i!=k)

 

f o r ( j = 0 ; j < m ; j + + )

 

t e m p [ t ] [j ] = m a t r [ i ] [j ]

 

t+ + ;

 

}

//у д а л е н и е

ст а р о й матрицы

 

f o r ( i = 0 ; i < n ; i + + )

 

d e l e t e m a t r [ i ] ;

 

d e l e t e []

m a tr;

 

n - - ;

//у м ен ьш аем

к о л и ч е с т в о стр о к

//п е ч а т ь

новой матрицы

 

f o r (1 = 0 ;i< n ;i+ + )

{

f o r (j= 0 ;j< m ;j+ + )

c o u t< < te m p [ i] [ j ] « ,f c o u t « " \ n " ;

}

}

Соседние файлы в папке книги