Программирование >>  Инициализация объектов класса, структура 

1 ... 26 27 28 [ 29 ] 30 31 32 ... 395


string *ps;

синтаксически правильны и совершенно эквивалентны:

string* ps;

Однако рекомендуется использовать первый вариант написания: второй способен ввести в заблуждение, если добавить к нему определение еще одной переменной через запятую:

внимание: ps2 не указатель на строку!

string* ps, ps2;

Можно предположить, что и ps, и ps2 являются указателями, хотя указатель - только первый из них.

Если значение указателя равно 0, значит, он не содержит никакого адреса объекта. Пусть задана переменная типа int:

int ival = 1024;

pi инициазирован нулев адресом

int *pi = 0;

pi2 инициализирован адресом ival int *pi2 = &ival;

правильно: pi и pi2 содержат адрес ival

pi = pi2;

pi2 содержит нулевой адрес

Ниже приводятся примеры определения и использования указателей на int pi и pi2:

pi2 = 0;

Указателю не может быть присвоена величина, не являющаяся адресом:

double *dp;

Указатель обозначается звездочкой перед именем. В определении переменных списком звездочка должна стоять перед каждым указателем (см. выше: ip1 и ip2). В примере ниже lp - указатель на объект типа l ong, а lp2 - объект типа l ong:

long *lp, lp2;

В следующем случае fp интерпретируется как объект типа float, а fp2 - указатель на него:

float fp, *fp2;

Оператор разыменования (*) может отделяться пробелами от имени и даже непосредственно примыкать к ключевому слову типа. Поэтому приведенные определения



double dval;

объекта другого типа. Если определены следующие переменные:

double *ps = &dval;

ошибки компиляции

то оба выражения присваивания, приведенные ниже, вызовут ошибку компиляции:

недопустимое присваивание типов данн: int* <== double*

pi = pd

pi = &dval;

Дело не в том, что неременная pi не может содержать адреса объекта dval - адреса объектов разных типов имеют одну и ту же длину. Такие операции смешения адресов запрещены сознательно, потому что интерпретация объектов компилятором зависит от типа указателя на них.

Конечно, бывают случаи, когда нас интересует само значение адреса, а не объект, на который он указывает (допустим, мы хотим сравнить этот адрес с каким-то другим). Для разрешения таких ситуаций введен специальный указатель void, который может

правильно: void* может содержать

указывать на любой тип данных, и следующие выражения будут правильны:

адреса бого типа

*pv = pi;

pv = pd;

Тип объекта, на который указывает void*, неизвестен, и мы не можем манипулировать этим объектом. Все, что мы можем сделать с таким указателем, - присвоить его значение другому указателю или сравнить с какой-либо адресной величиной. (Более подробно мы расскажем об указателе тина void в разделе 4.14.)

Для того чтобы обратиться к объекту, имея его адрес, нужно применить операцию разыменования, или косвенную адресацию, обозначаемую звездочкой (*). Имея

int ival = 1024;, ival2 = 2048;

следующие определения переменных:

int *pi = &ival;

мы можем читать и сохранять значение ival, применяя операцию разыменования к указателю pi:

ошибка: pi не может принимать значение int pi = ival

Точно так же нельзя присвоить указателю одного типа значение, являющееся адресом



int int

**ppi = &pi;

*pi2 = *ppi;

cout

ival << \n

<< Значение ival\n << явное значение: << косвенная адресация: << *pi << \n << дважды косвенная адресация: << **ppi << \n

ppi необходимо применить дважды.

<< endl;

Указатели могут быть использованы в арифметических выражениях. Обратите внимание на следующий пример, где два выражения производят совершенно различные действия:

int i, j, k;

int *pi = &i;

i = i + 2 *pi = *pi + 2;

увеличение адреса, содержащегося в pi, на 2 pi = pi + 2;

К указателю можно прибавлять целое значение, можно также вычитать из него. Прибавление к указателю 1 увеличивает содержащееся в нем значение на размер области памяти, отводимой объекту соответствующего типа. Если тип char занимает 1 байт, int - 4 и double - 8, то прибавление 2 к указателям на char, int и double увеличит их значение соответственно на 2, 8 и 16. Как это можно интерпретировать? Если объекты одного типа расположены в памяти друг за другом, то увеличение указателя на 1 приведет к тому, что он будет указывать на следующий объект. Поэтому арифметические действия с указателями чаще всего применяются при обработке массивов, в любых других случаях они вряд ли оправданы.

Вот как выглядит типичный пример использования адресной арифметики при переборе элементов массива с помощью итератора:

int ia[10];

косвенное присваивание переменной ival значения ival2

*pi = ival2;

косвенное использование переменной ival как rvalue и lvalue *pi = abs(*pi); ival = abs(ival);

*pi = *pi + 1; ival = ival + 1;

Когда мы применяем операцию взятия адреса (&) к объекту типа int, то получаем результат типа int*

int *pi = &ival;

Если ту же операцию применить к объекту типа int* (указатель на int), мы получим указатель на указатель на int, т. е. int**. int** - это адрес объекта, который содержит адрес объекта типа int. Разыменовывая ppi, мы получаем объект типа int*, содержащий адрес ival. Чтобы получить сам объект ival, операцию разыменования к



1 ... 26 27 28 [ 29 ] 30 31 32 ... 395

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика