На рисунках 4-8 показаны состояния программы 1.2 после выполнения 7-й, 9-

Й, 15-16-й, 21-й и 25-й строк.

Рис. 4.. Состояние про-

Граммы 1.2 после выпол-

Нения 7-й строки с описа-

Ниями переменных.

Рис. 5.. Программа 1.2

После выполнения опера-

Торов присваивания в 9-й,

Й и 11-й строках.

Рис. 6.. Программа 1.2

После выполнения опера-

Торов присваивания в 15-

Й и 16-й строках.

78

Рис. 7.. Программа 1.2 после

Выполнения оператора присваи-

Вания в 21-й строке.

Рис. 8.. Программа 1.2 после вы-

полнения оператора "delete" в

Й строке.

Состояния на рис. 4 и рис. 8 похожи тем, что значения указателей "ptr_a" и

"ptr_b" не определены, т.е. они указывают на несуществующие объекты. Обратите

внимание, что указатель "ptr_b" в конце программы оказывается в неопределенном

состоянии, хотя при вызове оператора "delete" этот указатель явно не передавался.

Если указатель "ptr" указывает на несуществующий объект, то использование

в выражениях значения "*ptr" может привести в непредсказуемым (и часто катастро-

фическим) результатам. К сожалению, в Си++ нет встроенного механизма проверки

Несуществующих указателей. Программист может сделать свои программы более

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

нулевой адрес (символическое имя нулевого адреса – "NULL"). Для хранения перемен-

ных в Си++ нулевой адрес не используется.

Константа "NULL" (целое число 0) описана в библиотечном заголовочном файле

"stddef.h". Значение "NULL" можно присвоить любому указателю. Например, в про-

Грамме 1.3, являющемся усовершенствованным вариантом программы 1.2, для защи-

ты от использования неопределенных указателей "*ptr_a" и "*ptr_b" были добавлены

следующие строки:

#include <iostream.h>

#include <stddef.h>

...

...

delete ptr_a;

ptr_a = NULL;

delete ptr_b;

ptr_b = NULL;

...

...

if (ptr_a!= NULL)

{

*ptr_a =...

...

...

Фрагмент программы 1.3.

В случае, если для создания динамической переменной не хватает свободной

оперативной памяти, то после вызова "new" Си++ автоматически присвоит соответст-

вующему указателю значение "NULL". В показанном ниже фрагменте программы 1.4

Этот факт используется для организации типичной проверки успешного создания ди-

Намической переменной.

#include <iostream.h>

#include <stdlib.h> /* "exit()" описана в файле stdlib.h */

#include <stddef.h>

...

...

79

ptr_a = new int;

if (ptr_a == NULL)

{

cout << "Извините, недостаточно оперативной памяти";

exit(1);

}

...

...

Фрагмент программы 1.4.

Указатели можно передавать в качестве параметров функций. В программе 1.5

Проверка на корректность указателя выполняется в отдельной функции, выполняю-

щей создание динамической целочисленной переменной:

void assign_new_int(IntPtrType& ptr)

{

ptr = new int;

if (ptr == NULL)

{

cout << "Извините, недостаточно оперативной памяти";

exit(1);

}

}

Фрагмент программы 1.5.

2. Переменные типа "массив". Арифметические операции с указателями

В 6-й лекции были рассмотрены массивы – наборы однотипных переменных. В

Си++ понятия массива и указателя тесно связаны. Рассмотрим оператор описания:

int hours[6];

Этот массив состоит из 6-ти элементов:

hours[0] hours[1] hours[2] hours[3] hours[4] hours[5]

Массивы в Си++ реализованы так, как будто имя массива (например, "hours")

Является указателем. Поэтому, если добавить в программу объявление целочисленно-

го указателя:

int* ptr;

то ему можно присвоить адрес массива (т.е. адрес первого элемента массива):

ptr = hours;

После выполнения этого оператора обе переменные – "ptr" и "hours" – будут

указывать на целочисленную переменную, доступную в программе как "hours[0]".

Фактически, имена "hours[0]", "*hours" и "*ptr" являются тремя различными

именами одной и той же переменной. У переменных "hours[1]", "hours[2]" и т.д.

также появляются новые имена:

*(hours + 1) *(hours + 2)...

Или

*(ptr + 1) *(ptr + 2)...

В данном случае "+2" означает "добавить к адресу указателя смещение, соот-

ветствующее 2-м целым значениям". Из арифметических операций к указателям часто

Применяется сложение и вычитание (в том числе операции инкремента и декремента

80

"++" и "--"), а умножение и деление не используются. Значения однотипных указате-

Лей можно вычитать друг из друга.

Главное, что нужно запомнить относительно сложения и вычитания значений

из указателя – в выражениях Си++ указывается не число, которое нужно вычесть (или

Добавить) из адреса, а количество переменных заданного типа, на которые нужно

"сместить" адрес.

Арифметические выражения с указателями иногда позволяют более кратко за-

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

Английской строки в верхний регистр (фрагмент программы 2.1).

void ChangeToUpperCase(char phrase[])

{

int index = 0;

while (phrase[index]!= '\0')

{

if (LowerCase(phrase[index]))

ChangeToUpperCase(phrase[index]);

index++;

}

}

Bool LowerCase(char character)

{

return (character >= 'a' && character <= 'z');

}

void ChangeToUpperCase(char& character)

{

character += 'A' - 'a';

}

Фрагмент программы 2.1.

Обратите внимание на полиморфизм функции "ChangeToUpperCase(...)" – при

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

ные параметры (у одной – параметр типа "char", а у другой – параметр типа "сим-

вольный массив"). Имя массива "phrase" является переменной-указателем, поэтому

Функцию с параметром-массивом можно переписать короче, если использовать

арифметические выражения с указателями:

void ChangeToUpperCase(char* phrase)

{

while (*phrase!= '\0')

{

if (LowerCase(*phrase))

ChangeToUpperCase(*phrase);

phrase++;

}

}

Фрагмент программы 2.2.

Эта модификация функции не влияет на остальные части программы – вызовы

Вариантов функций с параметром-указателем или параметром-массивом записывают-

ся одинаково, например:

81

char a_string[] = "Hello World";

...

...

ChangeToUpperCase(a_string);

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

Правила создания и уничтожения динамических переменных типов "int",

"char", "double" и т.п. (см. п.1.3) распространяются и на динамические массивы. По

Отношению к массивам динамическое распределение памяти особенно полезно, по-


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: