int i = 7;
int* pi = &i; //Указатель на int. Проинициализирован адресом i.
int** ppi = &pi //Указатель на int*, т.е. указатель на указатель на int. Проинициализирован адресом pi.
Когда-то я не понимал, что физически, указатель - это переменная хранящая адрес. Вот и все. Взглянем на структуру памяти после выполнения вышеприведенного кода.
Прямоугольники символизируют ячейки памяти. Правее указаны их адреса. Разумеется адреса могут быть другими, при следующем запуске. Стрелки добавлены исключильно для наглядности.
Переменная i может хранить любое значение типа int. pi - адрес любой переменной int. ppi - адрес любой переменной типа int*(например адрес pi).
Все это легко проверяется. В моем примере:
cout << i << (long)&i; //7 2293572
cout << pi << (long)π //2293572 2293568
cout << ppi << (long)&ppi; //22968 2293564
cout << *pi << **ppi << (long)*ppi; //7 7 2293572
(long) - преобразование перед выводом. Без него адреса выводятся в шеснадцатеричном виде. * - операция разыменования. То есть *pi возвращает значение переменной, находящуюся по адресу pi. *ppi == pi == &i
Был еще сложный для меня момент: указатели на int и на int* (и вообще все указатели) так похожи, раз хранят только адрес. Почему же нельзя сделать просто указатель на переменную любого типа. Вообще говоря можно. Но в таком случае можно легко ошибиться. Например случайно указать на переменную int, и думать про себя, что это объект. А благодаря типизации, компилятор будет следить за этим, а среда разработки будет помогать автозавершением кода для более сложных типов.
Уф, пока писал и проверял, сам все понял :)
Хороший пример, я тоже попробовал написать простенькую программку и разобрался. Но возник интересный вопрос. Смотри:
ОтветитьУдалитьint a = 7;
int* b = &a;
int** c = &b;
int*** d = &c;
cout << ***d << endl;
До какого момента вот так можно создавать указатель на указатель? Должен же быть какой-то предел?
Думаю, что хватит всем :) Даже указатель 300-го уровня работает нормально. Вообще физически все указатели это одно и то же, просто 4-байтовое (на 32-битных системах) число, а эти все звездочки просто для большей наглядности. Можно вообще обойтись одним void* и приводить его к чему нужно, но так легко запутаться и проще отдать это на откуп компилятору.
ОтветитьУдалить