Урок 7.1.5. Инициализация указателей

7.1.5. Инициализация указателей

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

int *р1 = (int *)1242, *р2 = (int *)1246;
// где int* операция преобразования целого значения
// к указателю на целое значение (к адресу)

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

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

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

float r, b;
float *ukr = &r, *ukb = &b;

В таком описании указателей с инициализацией ничего странного нет, ибо, как уже известно, &r и &b являются константами типа указатель. Эти же указатели можно инициализировать и в ходе выполнения программы адресами уже описанных переменных, например так:

float r, b;
float *ukr, *ukb;
. . . . . . . . .
 ukr = &r; ukb = &b;

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

char *uk_simv_rus, *uk_simv_lat;
char simvsmoll, simvlarge;
 . . . . . . . . .
 uk_simv_lat = &simvsmoll;
 uk_simv_rus = uk_simv_lat;

Обратим внимание на то, что в этом случае разные указатели содержат адреса одной и той же переменной. Значит, к переменной simvsmoll можно обратиться как через указатель uk_simv_rus так и через указатель uk_simv_lat, что зачастую предоставляет программисту дополнительные удобства.

Третий способ: использовать одну из встроенных функции распределения памяти, например, malloc. Обращение к функции malloc имеет вид:

malloc(<размер>);

Функция malloc выделяет (резервирует) из области “куча” (heap) память длиной <размер> байтов, где <размер> задается выражением типа unsigned. Функция возвращает адрес выделенной памяти в виде константы-указателя типа void.

Если в памяти нет под кучу свободного места требуемого размера, то в качестве результата будет выдан адрес 0 (NULL). Куча (engl. heap)– это память, выделяемая динамически или запрашиваемая статически у операционной системы. Эта память используется для размещения объектов, динамически созданных программой.)

Инициализацию указателя можно осуществить следующим образом:

int *x = (int *)malloc(sizeof(int)); .

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

Исходя из такой посылки, сформулируем наши вопросы и дадим на них ответы. Вопрос первый: является ли выражение справа от знака присваивания константным? Да, является, так как функция возвращает адрес выделенной памяти и можно считать, что обращение к функции есть именованная константа-указатель, значение которой устанавливается в момент применения функции.

Вопрос второй: почему в константном выражении использована операция преобразования типа (int *)? Потому что возвращается константа-указатель на неопределенный тип void, а мы инициализируем указатель на данные типа int. Следовательно, нам нужно преобразовать константу-указатель на неопределенный тип к константе-указателю на целый тип.

И, наконец, последний, третий вопрос: почему <размер> задан операцией sizeof(int)? Ответ: для того, чтобы обеспечить мобильность программы. В самом деле, если мы запишем этот оператор в виде int*x=(int*)malloc(2); , то при использовании компьютера другой архитектуры, где, например, данные типа int представляются четырехбайтными словам памяти, такая программа может оказаться неработоспособной, т.к. для четырехбайтного целого резервируется два байта памяти.

С использованием функции malloc, инициализацию описанного указателя можно осуществлять в нужный момент и в ходе выполнения программы:

int *x;
. . . .
x = (int *)malloc(sizeof(int);
if (x != 0) //если malloc вернула 0, то память не выделилась
{
 //действия программы, если память выделена
*х =- 5244;
 //в выделенную память послали -5244
}
else {
// действия программы, если память не выделена 
};

В языке С++ для выделения памяти используется операция new:

new <тип>
new <тип>[<количество элементов>]

Например:

int *x;
x = new int;     //выделить память под хранение данного типа int
x = new int[10]; //выделить память под хранение 10-ти данных типа int

Если операция new возвращает 0 (NULL), то это означает, что память не выделилась.

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

Поэтому если динамически выделяемая память не нужна, то ее следует освободить. Освобождение памяти осуществляется функцией free, обращение к которой имеет вид:

free(<указатель>);

где <указатель> должен содержать в себе адрес памяти, которая должна быть освобождена. Чтобы уничтожить в памяти значение -5244, динамически созданное в области кучи, как показано в предыдущем примере, нужно записать оператор free(x); .

В языке С++ для освобождения динамически выделенной памяти используется операция delete:

delete <указатель>;

delete [ ] <указатель>;

Например:

int * p;
int i = 10;
 
p= new int[i]; //выделить память под массив данных
if(p != NULL) printf("Память выделена");
else printf("Свободной памяти нет");
delete[] p;    //освободить память массива данных по адресу р
 
int *x;
x = new int;
delete x;  // освободить память по адресу x

 

рассказать друзьям и получить подарок

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов