Теперь можно обратиться к тому объекту, адрес которого содержит pint, используя операцию раскрытия указателя, называемую косвенной адресацией.
*pint = *pint+1; //увеличивает ival на 1, т.е. эквивалентно ival=ival+1;
В этом примере мало смысла. В реальности указатели используются чаще всего для манипуляций с динамически размещаемыми объектами.
Выделяют два основных различия между статическим и динамическим выделением памяти:
· Статические объекты обозначаются именованными переменными, и действия над этими объектами производятся напрямую, с использованием их имен. Динамические объекты не имеют собственных имен, и действия над ними производятся косвенно, с помощью указателей.
· Выделение и освобождение памяти под статические объекты производится компилятором автоматически. Выделение и освобождение памяти под динамические объекты целиком и полностью возлагается на программиста. Для манипуляции динамически выделяемой памятью служат операторы new и delete.
Простейшее объявление указателя на объект имеет вид: тип *имя; Тип может быть любым, причем тип к этому моменту может быть только объявлен, но не определен.
Каждый указатель ассоциируется с некоторым типом данных, причем их внутреннее представление не зависит от внутреннего типа: и размер памяти, занимаемый объектом типа указатель, и диапазон значений у них одинаков. Указатели на разные типы могут иметь одно и то же значение, но область памяти, где размещаются соответствующие типы может быть различной:
· Указатель на int, содержащий значение адреса 1000, указывает на область памяти 1000-1003 (в 32-битной системе);
· Указатель на double, содержащий значение адреса 1000, указывает на область памяти 1000-1007 (в 32-битной системе);
int *ip1, *ip2;
string *pstring;
double *dp;
long *lp, lp2; //lp – указатель на объект типа long, lp2 – переменная типа long/
Синтаксически правильны и эквивалентны следующие определения:
string *ps;
string* ps;
Рекомендуется использовать первый вариант определения. Если значение указателя равно нулю, значит он не содержит никакого адреса объекта. Указатель может быть константой, а также указывать на константу. Примеры:
Int i; //Целая переменная
const int ci=1; //Целая константа
const int *pci; //указатель на целую константу
int * const cp=&i; //указатель константа на целую переменную
const int *const cpc=&ci; //указатель-константа на целую константу
Как видно из примеров, модификатор const, находящийся между именем указателя и звездочкой, относится к самому указателю и запрещает его изменение, а const слева от звездочки задает постоянство значения, на которое он указывает.
Величины типа указатель подчиняются общим правилам определения области действия, видимости и времени жизни.
Инициализация указателей
При определении указателя необходимо стремиться выполнить его инициализацию, то есть присвоение начального значения. Инициализатор записывается после имени указателя либо в круглых скобках, либо после знака равенства.
Существуют следующие способы инициализации указателя:
1. Присвоение указателю адреса существующего объекта:
· С помощью операции получения адреса:
int a=5;
int *p=&a; или int *p(&a);
§ С помощью значения другого инициализированного указателя: int *r=p;
2. Присвоение указателю адреса области памяти в явном виде:
char *cp = (char *)0xB8000000; //шестнадцатеричная константа 0xB8000000
//преобразуется к типу указатель на char
3. Присваивание пустого значения:
int *ip=NULL; //константа NULL – указатель равный 0
int *ip=0; //рекомендуется использовать 0
Пустой указатель можно использовать для проверки, ссылается указатель на конкретный объект или нет.
4. Выделение участка динамической памяти и присваивание ее адреса указателю:
int *ip=new int; //new выделяет участок динамической памяти, достаточный для
//размещения величины типа int и записывает адрес начала этого
//участка в переменную ip.
int *ip1=new int(10); //выделенная динамическая память инициализируется значением 10
int *ip3=new int[10]; //выделяется память под массив из 10 величин типа int и записывает
//адрес начала этого участка в переменную ip3.
Освобождение памяти должно выполняться с помощью операции delete: delete ip; delete ip1; delete []ip3;
Операции над указателями
& или операция адресации – унарная операция, возвращает адрес своего операнда.
int y=5;
int *ip=&y; //адрес переменной y присваивается укзателю ip
* или операция косвенной адресации или операция разыменования – возвращает значение, находящееся в области памяти, на которую указывает указатель (операнд операции *)
cout<<*ip; //выведет на экран 5
*ip=9 //9 присвоится переменной Y
cin>>*ip //осуществляет ввод значения переменной y
Пример.
int a=7;
int *aPtr=&a;
cout<<&*aPtr<<*&aPtr; //выведет 77, что показывает что операции * и & инверсны друг другу.
Когда мы применяем операцию & (взятия адреса) к объекту типа int, то получаем результат типа int *. Если ту же операцию применить объекту типа int* (указатель на int), то получаем результат типа int** (указатель на указатель на int). Объект типа int** - это адрес объекта, который содержит адрес объекта типа int.
int **aaPtr=&aPtr;
Чтобы получить само значение объекта a, необходимо операцию разыменования применить дважды:
cout<<**aaPtr;