|
Программирование >> Инициализация объектов класса, структура
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 = π *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, операцию разыменования к
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |