АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Упражнение. Что делает следующий фрагмент кода?

Читайте также:
  1. Групповое упражнение.
  2. Дидактическое упражнение «Найди причину»
  3. Духовное упражнение
  4. Духовное упражнение
  5. Духовное упражнение
  6. Духовное упражнение
  7. Духовное упражнение
  8. Духовное упражнение
  9. Духовное упражнение
  10. Духовное упражнение «Единство с Живой»
  11. Единственное начальное упрощенное упражнение
  12. Исцеление с помощью глаз - упражнение «Глаз Световида»

Что делает следующий фрагмент кода? В чем состоит логическая ошибка?

int *pi = new int(10);int *pia = new int[10];
while (*pi < 10) {
pia[*pi] = *pi; *pi = *pi + 1;
}delete pi;delete[] pia;

 

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

  • указатель на int, содержащий значение адреса 1000, направлен на область памяти 1000-1003 (в 32-битной системе);
  • указатель на double, содержащий значение адреса 1000, направлен на область памяти 1000-1007 (в 32-битной системе).

Вот несколько примеров:

int *ip1, *ip2;complex<double> *cp;string *pstring;vector<int> *pvec;double *dp;

Указатель обозначается звездочкой перед именем.

В примере ниже lp – указатель на объект типа long, а lp2 – объект типа long:

long *lp, lp2;

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

float fp, *fp2;

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

string *ps;string* ps;

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

//внимание: ps2 не указатель на строку!string* ps, ps2;

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

 

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

int ival = 1024;

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

//pi инициализирован нулевым адресомint *pi = 0;
// pi2 инициализирован адресом ival
int *pi2 = &ival;
// правильно: pi и pi2 содержат адрес ival
pi = pi2;
// pi2 содержит нулевой адрес
pi2 = 0;

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

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

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

double dval;double *ps = &dval;

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

// ошибки компиляции// недопустимое присваивание типов данных: int* <== double*pi = pdpi = &dval;

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

// правильно: void* может содержать// адреса любого типаvoid *pv = pi;pv = pd;

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

 

 

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

int ival = 1024;, ival2 = 2048;int *pi = &ival;

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

// косвенное присваивание переменной 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, операцию разыменования к ppi необходимо применить дважды.

int **ppi = &pi;int *pi2 = *ppi;
cout << "Значение ival\n" << "явное значение: " << ival << "\n"
<< "косвенная адресация: " << *pi << "\n"
<< "дважды косвенная адресация: " << **ppi << "\n"

<< 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 приведет к тому, что он будет указывать на следующий объект. Поэтому арифметические действия с указателями чаще всего применяются при обработке массивов; в любых других случаях они вряд ли оправданы.
Вот как выглядит типичный пример использования адресной арифметики при переборе элементов массива с помощью итератора:


1 | 2 | 3 | 4 | 5 | 6 | 7 |

Поиск по сайту:



Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Студалл.Орг (0.004 сек.)