Указатель – это объект, содержащий адрес другого объекта и позволяющий косвенно манипулировать этим объектом. Скажем, определена статическая переменная
Указатели
Прежде чем написать сколько-нибудь сложную программу, необходимо научиться правильно выделять память во время выполнения и обращаться к ней. В С++ объекты могут быть размещены либо статически – во время компиляции, либо динамически – во время выполнения программы, путем вызова функций из стандартной библиотеки. Статическое размещение более эффективно, так как выделение памяти происходит до выполнения программы, однако оно гораздо мене гибко, потому что необходимо заранее знать тип и размер размещаемого объекта. К примеру, совсем не просто разместить содержимое некоторого текстового файла в статическом массиве строк: нам нужно заранее знать его размер. Задачи, в которых нужно хранить и обрабатывать заранее неизвестное число элементов, обычно требуют динамического выделения памяти. С++ имеет встроенный тип указатель, содержащий адрес переменной.
Указатель – это объект, содержащий адрес другого объекта и позволяющий косвенно манипулировать этим объектом. Скажем, определена статическая переменная
int ival=1024;
Когда мы пишем
int ival2=ival+1;
то обращаемся к значению, содержащемуся в переменной ival, прибавляем к нему 1 и инициализируем переменную ival2 этим новым значением. Можно обратиться к адресу, по которому размещена переменная. Для этого нужно объявить указатель, содержащий адрес переменной ival:
int *pint; //указатель на объект типа int
pint=&ival; //pint – адрес ival, & - операция взятии адреса
Теперь можно обратиться к тому объекту, адрес которого содержит 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
Операции над указателями
& или операция адресации – унарная операция, возвращает адрес своего операнда.
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;
А здесь
int i;
int *pi=&i;
*pi++; //или *p=*p+1 - увеличение значения переменной i на 1
Сложение (вычитание) с целой константой. Если указатель на определенный тип увеличивается или уменьшается на константу, его значение изменяется на величину этой константы, умноженную на размер объекта данного типа.
Разность двух указателей – это разность их значений, деленная на размер типа в байтах (в применении к массивам разность указателей на третий и шестой элементы равна 3). Суммирование двух указателей не допускается. Например, pi (указатель на int) содержит значение адреса 3000. Тогда оператор
pi=pi+2;
увеличит значение адреса с.о: 3000+2*4Байт, так как тип int занимает 4байта памяти. Если бы pi был указателем на начало массива типа int, то после выполнения той же операции pi указывал бы на третий элемент массива.
Сравнение. Указатели можно сравнивать, используя операции проверки равенства и отношения, но такое сравнение бессмысленно, если указатели не указывают на элементы одного и того же массива. При сравнении указателей сравниваются адреса, хранимые в указателях. Сравнение двух указателей, указывающих на один и тот же массив, может показать, например, что один указатель указывает на элемент массива с более высоким номером, чем другой указатель. Типичным использованием сравнения указателя является определение, равен ли указатель 0.
Замечания:
Использование арифметических действий с указателями, не ссылающимися на элементы массива, обычно является логической ошибкой, т.к. мы не можем предполагать, что две переменные одинакового типа хранятся в памяти вплотную друг к другу.
Операция приведения типов. Указатель можно присваивать другому указателю, если оба указателя имеют один и тот же тип. В противном случае нужно использовать операцию приведения типа, чтобы преобразовать значение указателя в правой части присваивания к типу указателя в левой части присваивания.
unsigned long int A=0Xcc77ffaa;
usigned short int *pint=(unsigned short int*) &A;
unsigned char *pchar=(unsigned char *)&A;
cout<<A<<*pint<<*pchar;
Этот пример выведет на экран строку:
Cc77ffaa ffaa aa
Значения указателей pint и pchar одинаковы, однако разадресация дает разный результат: для pchar – один младший байт, для pint – два младших байта. При инициализации указателей была использована операция явного приведения типа: перед именем переменной в скобках указывается тип, к которому ее требуется преобразовать. При этом не гарантируется сохранение информации. И вообще, такой оператор приведения типа является устаревшим, хотя допускается стандартом С++. Но сами разработчики С++ не рекомендуют использовать эту устаревшую форму. Операторы явного преобразования типов, введенные в С++, будут рассмотрены позже.
Указатели
Прежде чем написать сколько-нибудь сложную программу, необходимо научиться правильно выделять память во время выполнения и обращаться к ней. В С++ объекты могут быть размещены либо статически – во время компиляции, либо динамически – во время выполнения программы, путем вызова функций из стандартной библиотеки. Статическое размещение более эффективно, так как выделение памяти происходит до выполнения программы, однако оно гораздо мене гибко, потому что необходимо заранее знать тип и размер размещаемого объекта. К примеру, совсем не просто разместить содержимое некоторого текстового файла в статическом массиве строк: нам нужно заранее знать его размер. Задачи, в которых нужно хранить и обрабатывать заранее неизвестное число элементов, обычно требуют динамического выделения памяти. С++ имеет встроенный тип указатель, содержащий адрес переменной.
Указатель – это объект, содержащий адрес другого объекта и позволяющий косвенно манипулировать этим объектом. Скажем, определена статическая переменная
int ival=1024;
Когда мы пишем
int ival2=ival+1;
то обращаемся к значению, содержащемуся в переменной ival, прибавляем к нему 1 и инициализируем переменную ival2 этим новым значением. Можно обратиться к адресу, по которому размещена переменная. Для этого нужно объявить указатель, содержащий адрес переменной ival:
int *pint; //указатель на объект типа int
pint=&ival; //pint – адрес ival, & - операция взятии адреса