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

Яп

.pdf
Скачиваний:
28
Добавлен:
15.03.2023
Размер:
6.44 Mб
Скачать

Указатели на указатели

intvar = 123; // инициализация переменной var числом 123 int*ptrvar= &var; // указатель на переменную var int**ptr_ptrvar = &ptrvar; // указатель на указатель на var int***ptr_ptr_ptrvar = &ptr_ptrvar; // ук. на ук. на указатель

cout <<var; //123 cout <<*ptrvar; //123

cout <<**ptr_ptrvar; // два раза разименовываем указатель //123 cout <<***ptr_ptr_ptrvar;; // указатель третьего порядка //123

cout <<"\n ***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> "<< var << endl;

***ptr_ptr_ptrvar -> **ptr_ptrvar -> *ptrvar -> var -> 123

cout <<"\t " << &ptr_ptr_ptrvar<< " -> " << " " << &ptr_ptrvar <<" ->" << &ptrvar <<" -> " << &var <<" -> " << var << endl;

0x22ff00 -> 0x22ff04 ->0x22ff08 -> 0x22ff0c -> 123

201

Указатель-константа и указатель на константный

указуемый объект

int i1, i2;

int * const p1 = &i1; // указатель-константа. Переназначить на другую область памяти нельзя, но значение переменной менять можно

const int* p2 = &i1; // указатель на const. Нельзя модифицировать значение переменной по этому адресу.

const int* const p3 = &i1; // указатель-константа на константу. Нельзя переназначить адрес, нельзя изменить значение по этому указателю.

p1 = &i2;

// ошибка, указатель-константа

 

*p1 = 5;

// правильно, ук. объект не является const

 

p2 = &i2;

// правильно, указатель не является const

 

*p2 = 5;

// ошибка, указуемый объект – константа

 

p3 = &i2;

// ошибка, указатель-константа

 

*p3 = 5;

// ошибка, указуемый объект - константа

203

2/27/2023

Динамические многомерные массивы

const unsigned int DIM1 = 3; const unsigned int DIM2 = 5;

int main() {

 

 

 

int **ary;

// Указатель для доступа к двумерному массиву

// создание

 

 

 

ary = new int *

[DIM1]; // выделяет память для массива указателей на int

for (int

i

= 0;

i

< DIM1; i++) { // В цикле каждый элемент массива указателей инициализируется

оператором new, который выделяет память для массива типа int.

ary[i]

=

new int [DIM2]; } // инициализация указателей

// работа с массивом

for (int

i

= 0;

i

< DIM1; i++) {

for (int j = 0;

j < DIM2; j++) {

ary[i][j] =

(i + 1) * 10 + (j + 1); } }

for (int

i

= 0;

i

< DIM1; i++) {

for (int

j = 0;

j < DIM2; j++) {

cout

<< setw(4) << ary[i][j]; }

cout << endl;

}

 

// уничтожение

 

 

for (int

i

= 0;

i

< DIM1; i++) {

delete

[] ary[i]; }

delete [] ary;

 

 

return 0;

}

 

 

202

Типизированные указатели

Типизированныеуказатели неявно могут быть преобразованыв указатели на void, но не обратно!!!

void*void_ptr;

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

int*int_ptr;

// типизированный ук. на тип int

char*char_ptr;

// типизированный указательна char

void_ptr= int_ptr;

// правильно

char_ptr= void_ptr;

// правильно в С, но ошибка С++

char_ptr= int_ptr;

// предупреждение в С, но ошибка в С++

204

51

Указатели на функции

Указатели могут ссылаться на функции. Имя функции, как и имя массива само по себе является указателем, то есть содержитадрес входа.

/*тип данных*/(*/*имя указателя*/)(/*список аргументовфункции*/);

Тип данных определяем такой, который будет возвращать функция, на которую будет ссылаться указатель. Символ указателя и его имя берутся в круглые скобочки, чтобы показать, что это указатель, а не функция, возвращающая указатель на определённый тип данных. После имени указателя идут круглые скобки, в этих скобках перечисляются все аргументы через запятую как в объявлении прототипа функции. Аргументы наследуются от той функции, на которую будет ссылаться указатель.

205

Зачем нужны указатели???

3.Доступ к большимструктурам данных.Используютдля косвенного обращения к большимструктурам данных, повышая скорость и сокращая затраты на организацию доступа.

int *ptr;

 

int a[100];

 

ptr= &a[0];

// явный адрес первого элемента

ptr=a;

// неявный адрес первого элемента

*(ptr+1) -эквивалентно a[i]

207

2/27/2023

Зачем нужны указатели???

1.Повышениеэффективности.Вместо копирования или пересылкив памяти большойструктуры данных можно скопировать или переслать толькоуказатель на эту структуру.

2.Динамическиеструктуры.С помощьюзаписей и указателей можнореализовать структуры данных, которые растут и сжимаются в период выполненияпрограммы. Например,такие как списки и деревья. Помимо элементов данных самой структуры, узел содержитодин или несколькоуказателей со ссылками на другие узлы.

Элемент связного списка Две секции:

водной секции (по имени info) содержится порция данных,

вдругой — адрес соседнего элемента структуры.

206

ССЫЛКИ

208

52

Ссылки

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

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

/*объявление ссылки*/; /*тип*/ &/*имя ссылки*/ =/*имя переменной*/;

Переменная,на которую ссылается ссылканазывается

ссылочнойпеременной.

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

209

Отличия указателей и ссылок

Основноеназначениеуказателя– это организация динамических объектов, то есть размер, которых может меняться (увеличиваться или уменьшаться).

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

Главное отличие состоит во внутреннеммеханизмеработы:

Указателиссылаютсянаучасток в памяти, используя его адрес.А ссылкиссылаютсяна объект, по его имени (тоже своего рода адрес).

211

2/27/2023

Ссылки

intvalue= 15;

int&reference= value; // инициализация ссылки значением переменной value

cout <<"value = " << value << endl; // value==15

cout <<"reference = " << reference << endl; // reference= =15

reference+=15; // изменяем значение переменной value посредством изменения значения в ссылке

cout <<"value= " << value << endl; // значение поменялось как в ссылке value==30 ;

cout <<"reference = " << reference << endl; // так и в ссылочной переменной reference= =30

210

ДИНАМИЧЕСКАЯ ПАМЯТЬ

212

53

Распределение памяти

Программа обычнопомещается в отдельную, непрерывную область.

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

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

Статические данные– переменные,объявленные в главной программеи других местах.

Куча – область данных, из которой данные динамически выделяют специфическими командами для зыка.

213

Дефрагментация кучи

1.Частичная дефрагментация. Если активные блоки кучи нельзя перемещать(или перемещениеобходится слишком дорого), то можно объединятьтолькосмежные блокииз списка свободного пространства.

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

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

С помощьювиртуальнойпамятименеджеркучи может продолжатьвыделениединамическойпамяти почти

215

бесконечно,не сталкиваясь с проблемойфрагментации.

2/27/2023

Распределение памяти

Менеджер кучи - это компонентисполняющей системы, который выделяети освобождаетпамять.

Это делается посредствомподдержкиспискасвободных блоков.

Когда сделан запрос на выделениепамяти, она ищется в этом списке, а при освобождении блок снова подсоединяетсяк списку свободных блоков.

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

Дефрагментациякучи - объединениесвободныхблоков в блокибольшегоразмера.

214

Работа с кучей

Си:

void malloc(size_tколичество_байтов); // выделить память

количество_байтовэто размер памяти,необходимой для размещения динамического объекта. Тип size_t задан в <stdlib.h> как некоторый целыйбез знака.

void free(void*p); // освободить память

p = malloc(128); if(!p) {

printf("Памяти не хватает!\n"); exit(1);

}

free(p);

Вызов функции free( ) с ошибочнымаргументом разрушит всю систему динамическогораспределения памяти. 216

54

Работа с кучей

В языке С++ для работы с динамическимиобъектами существуют операторы new и delete[].

Пример: int ptr1;

int ptr2 = newint[256];// Размещение динамического массива

ptr1 = ptr2;

 

delete [] ptr2;

// Удаление динамического массива

217

Повисшие указатели и утечки

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

К повисшемууказателю приводитоператорdispose() в

Pascal, delete в С, С++.

Повисшие указателипонижаютбезопасностьпрограмм.

Память, которая распределена,но недоступна, называется

мусором(garbage).

Говорят,что программы,которые создают мусор, дают утечки памяти (memoryleaks).

К утечкам памяти могут привести присваивания между указателями.

Утечкапамяти может понизить производительность,но не безопасность.

219

2/27/2023

УТЕЧКИ ПАМЯТИ И ПОВИСШИЕ УКАЗАТЕЛИ

218

Повисшие указатели и утечки

p и q - указатели на какие-то элементы.

p:= q приводит к тому, что p и q указывают наодин и тот же элемент.

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

Выполним теперь оператор delete(p).Теперь оба указателя становятся «повисшими», поскольку хранят адрес уничтоженного объекта.

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

Пример на Си++

int * ptr1 = new int; // выделить первый блок int * ptr2 = new int; // выделить второй блок

ptr2 = ptr1; // второй блок теперь недоступен!!! Возникла повисшая ссылка

220

55

Повисшие указателии утечки. Способы устранения

1.Ручной. присваивать указателю (параметру оператора dispose или delete) значение nil или null.

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

2.Автоматический. Использование сборщика мусора, встроенного в некоторые языки программирования.

Задача сборщика мусора – повторно использовать мусор, идентифицируя недоступные блоки памяти и возвращая их менеджеру динамической памяти:

oдля каждого блока ведется счетчик текущего числа указателей, ссылающихся на этот блок, и автоматически освобождает блок, когда счетчик доходит до нуля.

oотмечает все доступные блоки и затем собирает немаркированные (а следовательно недоступные) блоки.

221

ПОДПРОГРАММЫ

Подпрограмма– группа операторов, которая можетбыть выполненанеоднократно.

Т.о. её можнорассматривать как:

1.Сегмент программы,который должен выполнятьсяна разных стадиях вычисления, можетбыть написан один раз в виде подпрограммы,а затем многократновыполняться.

2.Логическая единица декомпозиции программы

(компиляции).

223

2/27/2023

ПОДПРОГРАММЫ. ВЫЗОВ ПОДПРОГРАММ

222

ПОДПРОГРАММЫ

1.Функции - расширяют встроенные в язык операции, возвращают результат, который можноиспользоватьв выражениях, как правило вызываются из выражений.

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

В языке С используютсятолько функции.

Если функция не должна ничего возвращать то её тип обозначается как void.

В ранних языках программирования подпрограммымогли только выполнятьдействия (былипроцедурами),не имели имени и параметров.

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

224

56

ПОДПРОГРАММЫ. ОСНОВНЫЕ ПОНЯТИЯ

Описание(интерфейс)подпрограммы– указание её имени,

параметрови типа возвращаемого значения (если есть).

Имя подпрограммы– любойдопустимый идентификатор языка программирования,покоторому транслятор однозначновосстанавливает соответствие между вызывающим и вызываемым кодом.

Тело подпрограммы– группа операторов, из которых состоит подпрограмма.

Вызов подпрограммы– оператор, указывающий на необходимостьвыполненияподпрограммы.В большинстве языков программирования это указание имени(иногда предваряемоеключевым словомCALL) и фактических параметров.

225

ПОДПРОГРАММЫ. ПАРАМЕТРЫ

intsquare (intx)

// x – формальныйпараметр

{

 

return x x;

// в теле – формальные параметры

};

 

square(2);// Вызов функции, «2»- фактический параметр

• Различают три разновидностифактического параметра:

1.локальныйобъект данных,принадлежащийвызывающей программе;

2.нелокальныйобъектданных,видимыйиз вызывающей программы;

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

вызова. 227

2/27/2023

ПОДПРОГРАММЫ. ПАРАМЕТРЫ

Данные передаются подпрограммев виде последовательностизначений, называемых параметрами.

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

Это объявление, которое находитсяв объявлении подпрограммы.Вычисление в телеподпрограммыпишется в терминах формальныхпараметров.

Фактическиепараметры– конкретныезначения,

подставляемые на места формальныхпараметров подпрограммы.

Это значение, которое вызывающая программапередает подпрограмме.

226

ПОДПРОГРАММЫ. ПАРАМЕТРЫ

228

57

Преимущества подпрограмм

1.Уменьшениесложностипрограммирования.

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

2.Закрытостьреализациимодификация алгоритмавнутри подпрограммыне воздействует на остальную программу.

3.Модульность программразбиение программына небольшиекуски, которые можно рассматривать отдельно => подпрограммыпозволяют управлять болеекрупными программами.

4.Расширениевозможностейязыковпрограммирования-

создание библиотек. Например, математическиефункции типа sin( ), log( ) обеспечиваются библиотекой, как и процедуры ввода-вывода.

229

Методы передачи параметров

Сопоставлениемежду фактическими и формальными параметрами в вызове подпрограммыназывают передачей параметров.

Методы передачи параметровэто способы, которыми параметрыпередаются в/из подпрограммы.

Два способа, которые применяютсядля установления такого связывания:

1.Позиционное сопоставление.

2.Сопоставление по имени.

231

2/27/2023

Подпрограммы в разных языках

На языке С спецификация функциизаписывается в виде: float Fun(float A, intB)

Процедура на языке С оформляется как функция, которая не возвращает результат, ее заголовок долженначинаться с ключевого слова void:

void Proc(float A, intB, float C, int D)

Спецификация в языке Ada более наглядновыражает возможные различия в смысле параметров:

procedure Proc (A: in float; B: in integer; C: in out float; D: out float)

in - передача из фактического в формальныйпараметр, out - из формальногов фактический параметр, in out - двунаправленнаяпередача.

230

Позиционное сопоставление

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

Обычноколичество формальныхи фактических параметров должно совпадать,чтобы соответствие между ними было

взаимно однозначным.

Python, Ruby, Fortran , PHP, С++ и Ada формальныепараметры могут иметьзначения по умолчанию.

Используетсяесли формальномупараметру не передается никакого фактического параметра.

Python:

def salary(hours, tax_free= 1, hour_rate) # tax_free задан

my_money= salary(120,hour_rate= 10.0) # можно вызвать так

232

58

Сопоставление по имени

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

Недостаток - пользовательподпрограммыдолжен знать имена формальныхпараметров.

Ada: Sub(X => A, Y => 27);

Python:

adder (size = the_size, # size, list, sum - формальные

list = the_list, # the_size, the_list, the_sum - фактические sum = the_sum)

C++ - параметрыпо умолчанию указываются в конце списка float salary (inthours, float hour_rate, inttax_free= 1); my_money= salary(120,10.0);// tax_free можно не указывать

233

Передача параметров по значению

При передачепо значению формальныйпараметрх подпрограммыР(х) получаетзначениефактического параметра.

Любые изменения значения формального параметра, произошедшие во время выполнения подпрограммы, теряются, когда подпрограмма завершает свое выполнение.

В языках Pascal и С она рассматриваетсякак основной метод передачи параметров.

235

2/27/2023

МЕТОДЫ ПЕРЕДАЧИ ПАРАМЕТРОВ

234

Передача параметров по ссылке

Формальныйпараметрпревращается в синонимместа размещения фактического параметра. Т.е. подпрограмме становится доступным указатель на местоположениеэтого объекта (то есть его l-значение).

Пример C++

void swapc ( int px, int py) { intz;

z= px; px= py; py= z;

}

Вызов swapc(&a, &b) приводитк следующим действиям:

59

Передача параметров по ссылке

Пример C#

void adder(ref inttotal, intfee) { . . . } . . . # ref - по ссылке adder(ref count, additive);

Высокая эффективностькак времени, так и памяти. Не надо копировать значения фактических параметров, а затем отдельнообновлять их.

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

Могут возникнуть неумышленныеи ошибочные изменения фактических параметров.

Появляется возможность возникновенияпсевдонимов (алиасов) фактических параметров.Псевдонимы снижают читабельностьи надежность программ,а также усложняют их проверку.

237

Передача параметров по значению-результату

Доступ к обрабатываемым в подпрограмме параметрамускоряется, поскольку в косвенной адресации нет необходимости.

Безопасность хранения фактических параметров повышается, так как канал связи с ними открывается перед началомобработки в режиме приема и после окончания обработки в режиме записи. Во время выполнения тела подпрограммыканал закрыт.

Необходимость хранить параметрыв нескольких местах и тратить время на копирование их значений и адресов.

Сохраняется проблема псевдонимов.

239

2/27/2023

Передача параметров по значению-результату

Фактические параметры,не имеющие места размещения, передаются по значению.

Фактические параметры,имеющие место размещения, обрабатываются так:

1.Этап copy-in.Вычисляются значения и места размещения фактических параметров. Значения присваивают формальнымпараметрам.

2.Выполнениетела подпрограммы.Здесь обрабатываются формальныепараметры.

3.Этап copy-out.После выполнениятела подпрограммы конечные значения формальныхпараметров копируются обратнов места размещения, вычисленныена этапе copy-in.

238

Передача по результату

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

ПримерС# - спецификатор outпри форм.параметре

void Report(outinta, out intb) {a= 29; b = 57;} obj.Report(outm, outm);<- одинаковыеимена

если первымприсваиваетсязначениепараметраa, то значениефактическогопараметравызывающегомодуля - 57; если первымприсваиваетсязначениеb, тогда значениебудет равно29.

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

Моментдля вычисленияадреса фактическогопараметра может определятьсяв начальной фазе вызоваподпрограммыили при

возвращениииз нее - неопределенность должен снять

 

разработчик реализацииязыка.

240

60