Инициализация. При определении указателя надо стремиться выполнить его инициализации, т.е
При определении указателя надо стремиться выполнить его инициализации, т.е. присвоение начального значения. Непреднамеренное использование неинициализированных указателей – распространенный источник ошибок в программе. Инициализатор записывается после имени указателя либо в круглых скобках, либо после знака равенства. Существуют следующие способы инициализации указателей: 1. Присваивание указателю адреса существующего объекта: · с помощью операции получения адреса: int a = 5; //целая переменная int* p = &a; //в указатель записывается адрес а int* p (&a); //то же самое другим способом · с помощью значения другого инициализированного указателя int* r = p; · с помощью имени массива или функции, которые трактуются как адрес int b[10]; //массив int* t = b; //присваивание адреса начала массива … void f(int a){/*….*/}; //определение функции void (*pf) (int); //указатель на функцию pf = f; //присваивание адреса функции 2. Присваивание указателю адреса области памяти в явном виде: char* vp = (char*)0xB8000000; Здесь 0xB8000000 – шестнадцатеричная константа, (char*) операция приведения типа: константа преобразуется к типу «указатель на char». 3. Присваивание пустого значения: int* suxx = NULL; int* rulez = 0; 4. Выделение участка динамической памяти и присваивание ее адреса указателю: · с помощью операции new: int* n = new int; //1 int* m = new int (10); //2 int* q = new int [10]; //3 · с помощью операции malloc: int* u = (int *)malloc(sixeof(int)); //4
В первой строчке используется константа NULL, определенная в некоторых заголовочных файлах С, как указатель равный нулю. Рекомендуется использовать просто 0, так как это значение типа int будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать дл проверки, ссылается указатель на конкретный объект или нет. В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n. Память под саму переменную n (размера, достаточного для размещения указателя) выделяется на этапе компиляции. В операторе 2, кроме описанных выше действий, производится инициализация выделенной памяти значением 1. В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива. Через имя можно обращаться к любому элементу массива. Если память выделить не удалось, по стандарту должно порождаться исключение bad_alloc, старые версии компилятора могут возвращать 0. В операторе 4 делается то же самое, что и в операторе 1, но с помощью операции выделения памяти malloc, унаследованной из библиотеки С. В функцию передается один параметр – количество выделяемой памяти в байтах. Конструкция (int *) используется для приведения типа указателя, возвращаемого функцией, к требуемому типу. Если память выделить не удалось, то функция возвращает 0. Операцию new использовать предпочтительнее, чем malloc, особенно при работе с объектами. Освобождение памяти, выделенной с помощью операции new, должно производиться с помощью delete, а памяти, выделенной функцией malloc – посредством функции free. При этом переменная-указатель сохраняется и может инициализироваться повторно. Приведенные выше динамические переменные уничтожаются следующим образом: delete n; delete m; delete [] q; free (u); Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete []. Размерность массива при этом не указывается. Если квадратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся недоступны для дальнейших операций. Такие ячейки памяти называются мусором. Если переменная-указатель выходит из области своего действия, отведенная под нее память освобождается. Следовательно, динамическая переменная, на которую ссылается указатель, становится недоступной. При этом память из-под самой динамической переменной не освобождается. Другой случай появления «мусора» – когда инициализированному указателю присваивается значение другого указателя. При этом старое значение бесследно теряется. С помощью комбинации звездочек, круглых и квадратных скобок можно описывать составные типы и указатели на составные типы, например, в операторе int *(*p[10])(); объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int. По умолчанию квадратные и круглые скобки имеют одинаковый приоритет, больший, чем звездочка, и рассматриваются слева направо. Для изменения порядка рассмотрения используются круглые скобки. При интерпретации сложных описаний необходимо придерживаться правила «изнутри наружу»: · если справа от имени имеются квадратные скобки, это массив, если скобки круглые – функция; · если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию; · если справа встречается закрывающая круглая скобка, необходимо применить переменные выше правила внутри скобок, а затем переходить наружу; · в последнюю очередь интерпретируется спецификатор типа. Для приведенного выше описания порядок интерпретации указан цифрами: int *(*p[10])();
|