Урок 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 |
Оставить комментарий