Указатели – одно из интереснейших явлений С++. Это не менее интересное явление, чем рекурсия, о которой говорят, что, изучив однажды, программист часто напрочь о ней забывает, однако решения задач на основе рекурсии несут в себе скрытую красоту, хоть и требуют большего количества ресурсов от компьютера, чем итерации. Указатели же представляют собой ячейки памяти, в которых хранятся адреса переменных, что работают в программе. И только благодаря существованию указателей существует возможность написать массив данных, размер которого можно будет изменить в ходе выполнения программы, иначе массив в статическом варианте обладает размером лишь постоянного (константного) размера.
Чтобы начать работу с указателем, надо понимать на какой тип переменной мы хотим указать – целочисленный (int), с удвоенной точностью (double), символьный (char) или какой-либо другой. А чтобы объявить указатель, следует записать следующее int *pt, где int – целое число, на которое будем указывать, а *pt – собственно сам указатель pt. Далее, чтобы указатель начал работать с переменной целого типа, нам надо записать имя переменной со значком «амперсанда» (&), который укажет нам собственно на адрес, по которому хранится наша переменная. Пускай эта переменная – n, тогда запись вида:
int n;
int *pt=&n;
cout<<&n;
вернет нам адрес, который указывает на местоположение переменной n в соответствующей ячейке памяти компьютера. Перед этим, конечно же, нам следует объявить нашу переменную и после этого присвоить ее указателю. При этом говорят, что указатель pt указывает на адрес целочисленной переменной в памяти компьютера.
Чтобы обратиться не к адресу, по котором расположена переменная n, а собственно к значению самой переменной, следует применить операцию разыменования или записать *pt. Таким образом, мы сможем работать со значением самой переменной как-будто это и есть сама переменная n.
К работе с указателями, так же как и к работе с ссылками, необходимо привыкнуть, так как указатели, к примеру, очень частые гости аргументов функций и могут находится как внутри скобок функции, определяя вид ее сигнатуры, так и определять вид самой функции как указателя. Указатели можно применять и в шаблонных вариантах функции, так и в перегрузке функций.
Очень интересное отношение указателей и массивов. Имя массива собственно определяет адрес его первого элемента, что собственно делает указатель. Если выводить на экран компилятора mass[0], то мы получим значение первого элемента этого массива, однако если мы просто выведем mass, то в программе компилятора мы сможем увидеть адрес первого элемента массива. Поэтому можно записать:
int *ptmass=mass, а не int *ptmass=&mass;
Двигаясь по элементам массива с помощью указателя, прибавляя pt единичку (ptmass+1), мы не изменяем значение самого указателя – мы сдвигаемся на определенное число байтов в памяти, что выделяется под определенный тип переменной в определенной операционной системе. И если целочисленному типу переменной в памяти компьютера операционной системой выделяется 4 байта, то, как правило, мы сдвигаемся на 1*4 байта и попадаем на начало соседней первому элементу ячейки, у которой адрес отличается от адреса первого элемента на 4 байта. А чтобы узнать значение переменной по данному адресу, необходимо лишь разыменоваться и записать:
cout<<*(ptmass+1);
Посредством указателя мы легко можем создать не только статический вариант массива, но его динамический тип, записав:
int *mas=new int[a]; где a – размер массива, который можно изменить в процессе выполнения программы.
Существуют константные указатели, что указывают на какой-либо тип константного значения, а также указатели константного типа.
|