Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР15-С++24-мая-2012.doc
Скачиваний:
23
Добавлен:
23.09.2019
Размер:
1.07 Mб
Скачать

1.3. Операции над указателями

Для указателей-переменных разрешены некоторые операции: присваивание, инкремент, декремент, сложение, вычитание, сравнение.

Язык Си разрешает операцию сравнения указателей одинакового типа. При выполнении присваивания значение указателя в правой части выражения пересылается в ячейку памяти, отведенную для указателя в левой части.

Важной особенностью арифметических операций с указателями является то, что физическое увеличение или уменьшение его значения зависит от типа указателя, т.е. от размера того объекта, на который указатель ссылается. Если к указателю, описанному как type *ptr прибавляется или отнимается константа N, значение ptr изменяется на N*sizeof(type). Разность двух указателей type *ptr1, *ptr2 – это разность их значений, поделенная на sizeof(type).

Такие правила арифметических операций с указателями вытекают из того, что указатель в Си неявно рассматривается как указатель на начало массива однотипных элементов. Продвижение указателя вперед или назад совпадает с увеличением или уменьшением индекса элемента.

К указателям можно применять две унитарные операции:

1. & (взятие адреса).

Указатель получает адрес переменной. Данная операция применима к переменным, под которые выделен соответствующий участок памяти.

Например:

int *ptr, var=1; // ptr – указатель, var – переменная

ptr = &var; // В ptr заносится адрес var

2. * (операция разадресации).

Предназначена для доступа к значению, расположенному по данному адресу.

*ptr = 9; // В ячейку памяти, с адресом ptr записывается значение 9

var = *ptr; // Переменной var присваивается значение,

// расположенное по адресу ptr

Над указателями можно выполнять арифметические операции сложения, инкремента (увеличения на 1), вычитания, декремента (уменьшения на 1) и операции сравнения (>, >=, <, <=, ==, !=). При выполнении арифметических операций с указателями автоматически учитывается размер данных, на которые он указывает.

Например:

ptr++; // Сдвиг указателя ptr на один элемент вперед

(*ptr)++; // (или ++*ptr;) Увеличение на 1 значения переменной,

// на которую указывает указатель ptr

*ptr = NULL; // Очистка указателя рtr1

Указатели, как правило, используются при работе с динамической памятью (heap или «куча»). Для работы с динамической памятью в языке С определены следующие функции: malloc, сalloc, realloc и free.

В языке C++ для выделения и освобождения памяти определены операции new и delete соответственно. Используют две формы операций:

1. Тип *указатель = new тип (значение); – выделение участка памяти в соответствии с указанным типом и занесение туда заданного значения.

delete указатель; – освобождение выделенной памяти.

2. Тип *указатель = new тип[n]; – выделение участка памяти размером n блоков указанного типа.

delete [ ] указатель; – освобождение выделенной памяти.

Пример работы с одномерным динамическим массивом:

int *a; // Объявление указателя a

a = new int[n]; // Выделение n блоков памяти целого типа

… // Работа с массивом a

delete [] a; // Освобождение выделенной памяти

Над указателями определено 5 основных операций.

     Определение адреса указателя: &p, где p – указатель (&p – адрес ячейки, в которой находится указатель).

     Присваивание. Указателю можно присвоить адрес переменной p=&q, где p – указатель, q – идентификатор переменной.

     Определение значения, на которое ссылается указатель: *p (операция косвенной адресации).

     Увеличение (уменьшение) указателя. Увеличение выполняется как с помощью операции сложения (+), так и с помощью операции инкремента (++). Уменьшение – с помощью операции вычитания (–) либо декремента (––).

Например, пусть p1 – указатель, тогда р1++ перемещает указатель на:

o    1 байт, если *p1 имеет тип char;

o    4 байта, если *p1 имеет тип int (в 32 разрядной операционной системе) или 2 байта (в 16 разрядной операционной системе);

o    4 байта, если *p1 имеет тип float.

       Разность двух указателей. Пусть р1 и р2 – указатели одного и того же типа. Можно определить разность р1 и р2, чтобы найти, на каком расстоянии друг от друга находятся элементы массива.

Пример 15.12.программы.

Даны адреса переменных &a=63384,&b=64390,&c=64404. Что напечатает ЭВМ?

# include <stdio.h>

int main()

{

float a,*p1;

int b,*p2;

char c,*p3;

a=2.5; b=3; c='A';

p1=&a; p2=&b; p3=&c;

p1++; p2++; p3++;

printf("\n p1=%u, p2=%u, p3=%u",p1,p2,p3);

return 0;

}

Ответ: р1=63388, р2=64392, р3=64405.

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

Применительно к указателям на объект типа char операции адресной арифметики выполняются как обычные арифметические операции, потому что длина объекта char всегда равна 1.

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

При операции вычитания двух указателей можно определить количество объектов, расположенных между адресами, на которые указывают эти два указателя. При этом необходимо, чтобы указатели имели один и тот же тип. Кроме того, стандартом C допускается сравнение двух указателей. Как правило, сравнение указателей может оказаться полезным только тогда, когда два указателя ссылаются на общий объект, например, на массив.

Указатели можно сравнивать, причём не, только на равенство или неравенство, ведь адреса могут быть меньше или больше относительно друг друга. Разработаем программу, которая будет сравнивать адреса указателей.

// pointer.cpp: определяет точку входа для консольного приложения.

  

#include "stdafx.h"

#include <iostream>

using namespace std;

  int main(int argc, char* argv[])

{

    int var1 = 123; // инициализация переменной var1 числом 123

    int var2 = 99; // инициализация переменной var2 числом 99

    int *ptrvar1 = &var1; // указатель на переменную var1

    int *ptrvar2 = &var2; // указатель на переменную var2

    cout << "var1    = " << var1 << endl;

    cout << "var2    = " << var2 << endl;

    cout << "ptrvar1 = " << ptrvar1 << endl;

    cout << "ptrvar2 = " << ptrvar2 << endl;

    if (ptrvar1 > ptrvar2) // сравниваем значения указателей, то есть адреса переменных

        cout << "ptrvar1 > ptrvar2" << endl;

    if (*ptrvar1 > *ptrvar2) // сравниваем значения переменных, на которые ссылаются указатели

        cout << "*ptrvar1 > *ptrvar2" << endl;

    system("pause");

    return 0;

}

Результат работы программы (рис. 15. 2).

Рис. 15.2. Указатели в С++

В первом случае, мы сравнивали адреса  переменных, и, причём адрес второй переменной, всегда меньше адреса первой переменной. При каждом запуске программы адреса выделяются разные. Во втором случае мы сравнивали значения этих переменных используя операцию разыменования указателя.

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

Все остальные операции над указателями запрещены.