- •1. Роль указателей в языке с.
- •3. Определение указателя.
- •4. Объявление указателей.
- •5. Операции получения адреса и значения.
- •6. Правила использования основных операций.
- •7. Указатель на переменную и константу.
- •8. Инициализация указателей.
- •1. Присваивание указателю адреса существующего объекта:
- •9. Работа с указателями.
- •Int *pI;
- •Int I, *pI;
- •Int *pI;
- •Int I, *pI;
- •10. Задание, инициализация, вывод переменных и их указателей.
- •11. Ограничения на использование основных
- •12. Указатель константа.
- •13. Константы и ссылки/указатели
- •5 4 2 1 3 // Порядок интерпретации описания
- •16. Многоуровневая адресация
- •17. Операции над указателями.
- •18. Пустой указатель (null-указатель).
- •19. Ввод адреса с клавиатуры.
- •20. Разность значений указателей.
- •14. Присваивание указателей различного типа.
- •22. Явное преобразование типа указателя
- •23. Роль операции sizeof в управлении памятью
- •25. Работа с областью памяти переменного формата
14. Присваивание указателей различного типа.
Oперацию присваивания указателей различных типов следует понимать как назначение указателя в левой части на ту же самую область памяти, на которую назначен указатель в правой. Оба указателя после присваивания содержат один и тот же адрес. Но поскольку тип указуемых переменных у них разный, то эта область памяти по правилам интерпретации указателя будет рассматриваться как заполненная переменными либо одного, либо другого типа:
char *p, A[20];
int *q;
long *l;
p = A; q = p; l = p;
В этом примере p - указатель на область байтов, q - на область целых, l - на область длинных целых. Соответственно операции адресной арифметики *(p+i), *(q+i), *(l+i)
адресуют i-ый байт, i-ое целое и i-ое длинное целое от начала области:
+
q +-+
¦---------+
p +-+ +-+ +------->+-------+
¦---------------------->+ A ----+ ¦
+-+ ¦char 0 ¦ ¦int 0
+-------¦ ¦
¦char 1 ¦--¦
+-------¦ ¦int 1
¦char 2* ---------- *(p+2)
+-------¦* -------- *(q+1)
¦char 3 ¦--¦
+-------¦
*(p + 2) = 5; // записать 5 во второй байт области A
*(q + 1) = 7; // записать 7 в первое слово области A
Таким образом, область памяти имеет различную структуру (байтовую, словную и т.д.) в зависимости от того, через какой указатель мы с ней работаем. При этом неважно,
что сама область определена как массив типа char - это имеет отношение только к операциям с использованием идентификатора массива.
Присваивание значения указателя одного типа указателю другого типа сопровождается действием, которое называется в Си "ПРЕОБРАЗОВАНИЕ ТИПА УКАЗАТЕЛЯ". На самом деле это действие не приводит к каким-либо преобразованиями данных (команды транслятором не генерируются). Транслятор просто запо-минает, что тип указуемой переменной изменился и операции адрес-ной арифметики и косвенного обращения нужно выпол-нять с уче-том нового типа указателя. При присваивании происходит автома-тическое неявное преобразование типа указателя, которое в транс-ляторе "классического" Си обычносопровождается предупреждением.
Таким образом, операция присваивания указателя включает в себя:
- присваивание адреса от правого указателя к левому;
- неяное преобразование типа указателя от правого к левому.
22. Явное преобразование типа указателя
Рассмотрим три операции присваивания:
char *p, A[20];
int *q;
p = A;
q = p; *(p + 2) = 5;
*((q = p) + 2) = 5;
*((int *)p + 2) = 5;
// ¦ ¦ +-- тип указателя char* до преобразования
// ¦ +------ явное преобразование указателя
// ¦ на байт в указатель на целое
// +--------- тип указателя int* после преобразования
Все они дают одинаковый результат - записывают целое 5 во второе слово области памяти, определенной как массив байтов (символов) с именем A. Однако, если в первом и втором случае используется промежуточный указатель типа int*, то в последнем случае такой указатель создается как рабочая пере менная, которая получает тип int* и значение пере-мен-ной p. Такая операция называется явным преобразованием типа указателя. В скобках указывается абстрактный тип данных -указатель на требуемый тип (например, int*).