Арифметические действия с указателями




Указатели и ссылки

Понятие указателей

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

тип *имя_указателя

Например,

int *px;

double *yx;

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

int *px = 0; // указатель инициализируется нулем

int x;

int *px = &x; // указатель инициализируется адресом переменной х

Одноместная унарная операция & дает адрес объекта (переменной). В этом случае говорят, что px указывает на x. Одноместная операция * называется операцией ссылки по указателю или разыменовывания. Применяя ее к указателю, получаем объект (переменную), на который он указывает.

Если указатель px указывает на целую переменную x, выражение *px может фигурировать в любом контексте, в котором допускается x.

Пример. Присвоение указателям адресов переменных, косвенное обращение к переменным через указатели.

#include<iostream>

int main()

{

double a,b;

double *pa = &a;

double *pb = &b;

std::cout << "pa= " << pa << std::endl;

std::cout << "pb= " << pb << std::endl;

*pa = 5.55;

*pb = *pa + 6.57;

std::cout << "a= " << a << std::endl;

std::cout << "b= " << b << std::endl;

return 0;

}

Поскольку указатели сами являются переменными, их можно употреблять без разыменовывания.

Пример. Присваивание указателю значения другого указателя.

#include<iostream>

int main()

{

double a,b;

double *pa = &a;

double *pb = &b;

*pa = 5.55;

*pb = *pa + 6.57;

pb=pa;

return 0;

}

Теперь указатель pb указывает на такую же ячейку, что и pa, но значения переменных a и b не изменились!! Заметьте кардинальное отличие от операции *pb = *pa, когда меняется значение переменной b, но значение самого указателя остается неизменным. Проверить это можно, заменив строку pb = pa строкой *pb = *pa.

Следует заметить, что любой указатель может указывать только на объекты одного конкретного типа данных, заданного при его объявлении. Т.е. указатель типа double * не может указывать на тип int. Исключением является только указатель на тип void, в котором может содержаться произвольный адрес без указателя на тип данных. Однако по указателям этого типа нельзя ссылаться и получать значения.

Арифметические действия с указателями

С указателями можно использовать только четыре арифметических оператора: ++, --, +и-.

Чтобы лучше понять, что происходит при выполнении арифме­тических действий с указателями, начнем с примера. Пусть р1 — указатель на int-переменную с текущим значением 2000 (т.е. р1 содержит адрес 2000). После выполнения (в 32-разрядной среде) выражения p1++ содержимое переменной-указателя р1 станет равным 2004, а не 2001. Дело в том, что при каждом инкрементировании указатель р1 будет указывать на сле­дующее int-значение. Для операции декрементирования справедливо обратное утверждение, т.е. при каждом декрементировании значение р1 будет уменьшать­ся на 4. Например, после выполнения инструкции p1--; указатель р1 будет иметь значение 1996, если до этого оно было равно 2000.

Итак, каждый раз, когда указатель инкрементируется, он будет указывать на область памяти, содержащую следующий элемент базовоготипа этого указателя. А при каждом декрементировании он будет указывать па область памяти, содержа­щую предыдущий элемент базового типа этого указателя.

Для указателей на сим­вольные значения результат операций инкрементирования и декрементирования будет таким же, как при "нормальной" арифметике, поскольку символы занимают только один байт. Но при использовании любого другого типа указателя при инкре­ментировании или декрементировании значение переменной-указателя будет уве­личиваться или уменьшаться на величину, равную размеру его базового типа.

Выражение p1=p1+9 заставляет р1 ссылаться на девятый элемент базового типа указателя р1 относи­тельно элемента, на который р1 ссылался до выполнения этой инструкции.

Несмотря на то, что складывать указатели нельзя, один указатель можно вы­честь из другого (если они оба имеют один и тот же базовыйтип). Разность покажет количество элементов базового типа, которые разделяют эти два указателя.

Помимо сложения указателя с целочисленным значением и вычитания его из указателя, а также вычисления разности двух указателей, над указателями ника­кие другие арифметические операции не выполняются. Например, с указателями нельзя складывать float- или double-значения.

Пример.

#include <iostream>

int main ()

{

int A;

int *pA = 0;

A = 5;

pA = &A;

std::cout << "pA = " << pA << std::endl;

int B = – 32;

int *pB = &B;

std::cout << "pB = " << pB << std::endl;

int C = 101;

int *pC = &C;

std::cout << "pC = " << pC << std::endl;

std::cout << "pB-pA = " << pB - pA << std::endl;

std::cout << "pC-pB = " << pC - pB << std::endl;

std::cout << "pC-pA = " << pC - pA << std::endl;

getch ();

std::cout << "pA-pB = " << pA - pB << std::endl;

std::cout << "pB-pC = " << pB - pC << std::endl;

std::cout << "pA-pC = " << pA - pC << std::endl;

*(pB + 6) = 10;

std::cout << " A = " << A << std::endl;

return 0;

}

Результаты выполнения програмы:

pA = 0014F7D0

pB = 0014F7B8

pC = 0014F7A0

pB – pA = – 6

pC – pB = – 6

pC – pA = – 12

pA – pB = 6

pB – pC = 6

pA – pC = 12

A = 10

Таблица 2. Расположение переменных А и В в ячейках памяти.

B    
1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт
0014F7B8       0014F7BC       0014F7C0      

 

     
1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт
0014F7C4       0014F7C8       0014F7CC      

 

A    
1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт 1 байт
0014F7D0                      

Пример. Демонстрация работы операции увеличения указателя на целое число

#include <iostream>

int main ()

{int A;

int *pA = 0;

A = 5;

pA = &A;

std::cout << "pA = " << pA << std::endl;

int B = 35;

int *pB = &B;

std::cout << "pB = " << pB << std::endl;

std::cout << "pA-pB = " << pA - pB << std::endl;

*(pB + 6) = 12;

std::cout << " A = " << A << std::endl;

short int X = 6;

short int * pX = &X;

std::cout << "pX = " << pX << std::endl;

*(pX+12) = 23;

std::cout << " B = " << B << std::endl;

*(pX+13) = 23;

std::cout << " B = " << B << std::endl;

return 0; }

 

Результаты выполнения програмы:

pA = 0028FAB0

pB = 0028FA98

pA – pB = 6

A = 12

pX = 0028FA80

B =23!!!!!

B = 1507363!!!

Из результатов выполнения программы видно, что операции *(pX+12)=23 и *(pX+13)=23 приводят к косвенному обращению к ячейке памяти, содержащей переменную В. Результатом этого является потеря информации, содержащейся в переменной B.

 

Таблица 3. Расположение переменных Х и В в ячейках памяти.

X 24байта =12 B
0028FA80   0028FA98

 

Переменная int занимает 4 байта, а переменная short int 2 байта.



Поделиться:




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

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2018-01-08 Нарушение авторских прав и Нарушение персональных данных


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