![]() Главная страница Случайная страница КАТЕГОРИИ: АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника |
Инициализация указателей
Указатели чаще всего используют при работе с динамической памятью, называемой некоторыми эстетами кучей (перевод с английского языка слова heap). Это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится только через указатели. Время жизни динамических переменных — от точки создания до конца программы или до явного освобождения памяти. В C++ используется два способа работы с динамической памятью. Первый использует семейство функций malloc и достался в наследство от С, второй использует операции new и delete. При определении указателя надо стремиться выполнить его инициализацию, то есть присвоение начального значения. Непреднамеренное использование неинициализированных указателей — распространенный источник ошибок в программах. Инициализатор записывается после имени указателя либо в круглых скобках, либо после знака равенства. Существуют следующие способы инициализации указателя: 1. Присваивание указателю адреса существующего объекта: § с помощью операции получения адреса: int a = 5; // целая переменная int* р = & а; //в указатель записывается адрес а int* p (& а); // то же самое другим способом § с помощью значения другого инициализированного указателя: int* r = р; § с помощью имени массива или функции, которые трактуются как адрес (см. «Массивы» и «Передача имен функций в качестве параметров»: int b[10]; // массив int* t = b; // присваивание адреса начала массива … void f(int a){ /*... */ } // определение функции void (*pf)(int); // указатель на функцию pf = f; // присваивание адреса функции 2. Присваивание указателю адреса области памяти в явном виде: char* vp = (char *)0xB8000000; Здесь 0хВ8000000 — шестнадцатеричная константа, (char *) — операция приведения типа: константа преобразуется к типу «указатель на char». 3. Присваивание пустого значения: int* suxx = NULL; int* rulez = 0; В первой строке используется константа NULL, определенная в некоторых заголовочных файлах С как указатель, равный нулю. Рекомендуется использовать просто 0, так как это значение типа int будет правильно преобразовано стандартными способами в соответствии с контекстом. Поскольку гарантируется, что объектов с нулевым адресом нет, пустой указатель можно использовать для проверки, ссылается указатель на конкретный объект или нет. 4. Выделение участка динамической памяти и присваивание ее адреса указателю: § c помощью операции new: int* n = new int; // 1 int* m = new int (10); // 2 int* q = new int [10]; // 3 § с помощью функции malloc (< malloc.h>): int* u = (int *)malloc(sizeof(int)); // 4 В операторе 1 операция new выполняет выделение достаточного для размещения величины типа int участка динамической памяти и записывает адрес начала этого участка в переменную n. Память под саму переменную n (размера, достаточного для размещения указателя) выделяется на этапе компиляции. В операторе 2, кроме описанных выше действий, производится инициализация выделенной динамической памяти значением 10. В операторе 3 операция new выполняет выделение памяти под 10 величин типа int (массива из 10 элементов) и записывает адрес начала этого участка в переменную q, которая может трактоваться как имя массива. Через имя можно обращаться к любому элементу массива. Если память выделить не удалось, по стандарту должно порождаться исключение bad_a11oc (об исключениях рассказывается в разделе «Обработка исключительных ситуаций»). Старые версии компиляторов могут возвращать 0. В операторе 4 делается то же самое, что и в операторе 1, но с помощью функции выделения памяти malloc, унаследованной из библиотеки С. В функцию передается один параметр — количество выделяемой памяти в байтах. Конструкция (int*) используется для приведения типа указателя, возвращаемого функцией, к требуемому типу. Если память выделить не удалось, функция возвращает 0. Операцию new использовать предпочтительнее, чем функцию mallос, особенно при работе с объектами. Освобождение памяти, выделенной с помощью операции new, должно выполняться с помощью delete, а памяти, выделенной функцией malloc — посредством функции free. При этом переменная-указатель сохраняется и может инициализироваться повторно. Приведенные выше динамические переменные уничтожаются следующим образом: delete n; delete m; delete [] q; free (u); Если память выделялась с помощью new[], для освобождения памяти необходимо применять delete[]. Размерность массива при этом не указывается. Если квадратных скобок нет, то никакого сообщения об ошибке не выдается, но помечен как свободный будет только первый элемент массива, а остальные окажутся недоступны для дальнейших операций. Такие ячейки памяти называются мусором. ВНИМАНИЕ Если переменная-указатель выходит из области своего действия, отведенная под нее память освобождается. Следовательно, динамическая переменная, на которую ссылался указатель, становится недоступной. При этом память из-под самой динамической переменной не освобождается. Другой случай появления «мусора» — когда инициализированному указателю присваивается значение другого указателя. При этом старое значение бесследно теряется. С помощью комбинаций звездочек, круглых и квадратных скобок можно описывать составные типы и указатели на составные типы, например, в операторе int *(*p[10])(); объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int. По умолчанию квадратные и круглые скобки имеют одинаковый приоритет, больший, чем звездочка, и рассматриваются слева направо. Для изменения порядка рассмотрения используются круглые скобки. При интерпретации сложных описаний необходимо придерживаться правила «изнутри наружу»: 1) если справа от имени имеются квадратные скобки, это массив, если скобки круглые — это функция; 2) если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию; 3) если справа встречается закрывающая круглая скобка, необходимо применить приведенные выше правила внутри скобок, а затем переходить наружу; 4) в последнюю очередь интерпретируется спецификатор типа. Для приведенного выше описания порядок интерпретации указан цифрами: int *(*p[10])(); 5 4 2 1 3 // порядок интерпретации описания
|