Особенности объявлений
Инициализация указателей
Указатели чаще всего используют при работе с динамической памятью. Доступ к выделенным участкам динамической памяти, называемым динамическими переменными, производится только через указатели.
Время жизни динамических переменных — от точки создания до конца программы или до явного освобождения памяти.
В С++ используется два способа работы с динамической памятью. Первый использует семейство функций malloc и достался в наследство от С, второй использует операции new и delete.
Существуют следующие способы инициализации указателя:
1. Присваивание указателю адреса существующего объекта:
· с помощью операции получения адреса:
int a = 5;//целая переменная
int* p = &a;//в указатель записывается адрес 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;//шестнадцатиричная константа
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(sizeof(int));// 4
Освобождение памяти, выделенной с помощью операции new, должно выполняться с помощью delete, а памяти, выделенной функцией malloc — посредством функции free. При этом переменная-указатель сохраняется и может инициализироваться повторно. Приведенные выше динамические переменные уничтожаются следующим образом:
delete n; delete m; delete [] q; free (u);
ВНИМАНИЕ
Если переменная-указатель выходит из области своего действия, отведенная под нее память освобождается. При этом память из-под самой динамической переменной не освобождается.
Обозначения, используемые для производных типов, достаточно трудны для понимания лишь потому, что операции * и & являются префиксными, а [] и () - постфиксными. Поэтому в задании типов, если приоритеты операций не отвечают цели, надо ставить скобки. Например, приоритет операции [] выше, чем у *, и мы имеем:
int* v[10]; // массив указателейint (*p)[10]; // указатель массива Большинство людей просто запоминает, как выглядят наиболее часто употребляемые типы. Можно описать сразу несколько имен в одном описании. Тогда оно содержит вместо одного имени список отделяемых друг от друга запятыми имен. Например, можно так описать две переменные целого типа:int x, y; // int x; int y;Когда мы описываем производные типы, не надо забывать, что операции описаний применяются только к данному имени (а вовсе не ко всем остальным именам того же описания). Например:
int* p, y; // int* p; int y; НО НЕ int* y;int x, *p; // int x; int* p;int v[10], *p; // int v[10]; int* p;Но такие описания запутывают программу, и, возможно, их следует избегать.
Указатель может быть константой или переменной, а также указывать на константу или переменную. Рассмотрим примеры:
int i: // целая переменная
const int ci = 1; // целая константа
int * pi: // указатель на целую переменную
const int * pci; // указатель на целую константу
int * const cp = &i; // указатель-константа на целую переменную
const int * const cpc = &ci; // указатель-константа на целую константу
Как видно из примеров, модификатор const, находящийся между именем указа теля и звездочкой, относится к самому указателю и запрещает его изменение a const слева от звездочки задает постоянство значения, на которое он указывает.
С помощью комбинаций звездочек, круглых и квадратных скобок можно описывать составные типы и указатели на составные типы.
Например, в операторе
int *(*р[10] ) ( );
объявляется массив из 10 указателей на функции без параметров, возвращающих указатели на int.
По умолчанию квадратные и круглые скобки имеют одинаковый приоритет, больший, чем звездочка, и рассматриваются слева направо. Для изменения порядка рассмотрения используются круглые скобки.
При интерпретации сложных описаний необходимо придерживаться правила, «изнутри наружу»:
· если справа от имени имеются квадратные скобки, это массив, если скобки круглые — это функция;
· если слева есть звездочка, это указатель на проинтерпретированную ранее конструкцию;
· если справа встречается закрывающая круглая скобка, необходимо применить приведенные выше правила внутри скобок, а затем переходить наружу;
· в последнюю очередь интерпретируется спецификатор типа.
Для приведенного выше описания порядок интерпретации указан цифрами:
int * ( * p [ 10 ] ) ( ):
5 3 1 2 4 // порядок интерпретации описания
( Массив /1/ из 10 /2/ указателей /3/ на функции /4/ без параметров, возвращающих указатели на int/5/ ).