Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
програмирование..doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
612.86 Кб
Скачать

Лекция 12

Указатели

Указатель на языке С++ - это объект, значением которого является адрес. Во внутреннем представлении адрес может занимать 2 или 4 байта в зависимости от реализации языка.

Определение указателя имеет следующий синтаксис:

<type>*<имя указателя>;

Здесь <type> - это тип объекта, на который ссылается указатель; <имя указателя> -это идентификатор, * - операция обращения по адресу.

Например:

int *ptrint; // адрес целого числа, занимающего в памяти 2 байта

float *ptrfl; // адрес вещественного числа, занимающего в памяти 4 байта

char *ptrch; // адрес символа, занимающего в памяти 1 байт

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

Например:

char *p1, *p2, *p3;

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

<type>*<имя указателя>=<инициализирующее выражение>;

или

<type>*<имя указателя>(<инициализирующее выражение>);

В качестве инициализирующего выражения можно использовать:

- явно указанный адрес;

- указатель, имеющий значение;

- выражение, значением которого является адрес.

Если значение инициализирующего выражения равно 0, то оно преобразуется к пустому указателю NULL.

Примеры.

char *ptrchr =(char *)0xF000FFFF; // явно указанный адрес

int *ptrint(NULL);

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

&<операнд>

Здесь унарный оператор ‘&’ возвращает адрес операнда. Этот оператор применим к объектам, имеющим имя и размещенным в памяти компьютера.

Например:

float e = 15.4462;

float *pe = &e; // указателю ре присваивается адрес переменной е при его

// определении

После определения указателя ре к переменной е можно обращаться по её имени и с помощью адреса, являющегося значением указателя ре.

Например:

cout << “\n e = “ << e;

cout << “\n *pe = “ << *pe; //значение, записанное по адресу ре

Результат:

е = 15.4462 00

*ре = 15.4462 00

Если указатель получил нулевое значение, то обращаться по адресу нельзя.

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

*<имя указателя>

Это выражение можно использовать везде, где допустимо применение имен объектов того типа, к которому относится указатель.

Пример:

void main()

{

int t = 3200;

int *ptr = &t;

int value = *ptr; // присваивание значения с помощью указателя

cout << “value=” << value << ‘\n’;

}

Результат:

value = 3200

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

void *vp;

Такой указатель можно связывать с различными типами данных.

Пример.

#include <iostream.h>

void main()

{ void *vp;

int i = 77;

float e = 2.718282;

vp = &i; // настроились на целый тип int

cout << “\n *(int *)vp = ” << *(int *)vp;

vp = &e; // настроились на вещественный тип float

cout << “\n *(float *)vp = ” << *(float *)vp;

}

Результат:

*(int *)vp = 77

*(float *)vp =2.718282

В языке С++ разрешено присваивание указателю типа void * значения любого указателя, но не наоборот.

Пример.

void *vp;

int *ip;

….

ip = vp; // нельзя

….

vp = ip; // можно

….

Над указателями определены следующие арифметические операции:

- сложение указателя и константы;

- вычитание из указателя константы;

- вычитание указателей;

- инкремент;

- декремент.

Рассмотрим эти операции.

В языке С++ можно применять операцию вычитания к указателям одного типа и к указателю и целой const.

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

Разность указателей, адресующих два смежных объекта любого типа по абсолютной величине равна 1.

Пример.

#include <iostream.h>

void main()

{ int a = 1, b = 2;

int *pa = &a, *pb = &b;

cout << “\n pa = “ << pa; // адрес а

cout << “\n pb = “ << pb; // адрес b

cout << “\n (pa-pb) = “ << (pa-pb);

cout << “\n“ << (int)pa – (int)pb;

}

Результат:

pa = 0xfff4

pb = 0xfff2

(pa – pb) = 1

2

|| Существует различие между разностью однотипных указателей и разностью числовых значений этих указателей

|| Переменная, определенная в тексте программы позднее, имеет меньший адрес. Компилятор размещает объекты в обратном порядке, т.к. использует стек.

Из указателя можно вычитать целое число. При этом числовое значение указателя уменьшится на величину

k * sizeof(type)

где k – вычитаемое , type - тип объекта, к которому отнесён указатель.

Аналогично выполняется операция сложения указателя с целым числом.

|| В языке С++ запрещено суммирование двух указателей.

#include <iostream.h>

void main()

{ float zero = 0.0, pi = 3.1415, e = 2.7182;

float *ptr = &e;

cout << “\n ptr = “ << ptr << “*ptr = “ << *ptr;

cout << “\n (ptr+1) = “ << (ptr+1) << “*(ptr+1)=” << *(ptr+1);

cout << “\n (ptr+2) = “ << (ptr+2) << “*(ptr+2)=” << *(ptr+2);

}

Результат:

ptr = 0xffea *ptr = 2.7182

(ptr + 1) = 0xffee *(ptr+1) = 3.1415

(ptr+2) = 0xfff2 *(ptr+2)=0

Сравнение указателей

Указатели можно сравнивать, используя операции отношения ‘= =’ и ‘<’ и ‘>’.

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

Замечание.

Работать с указателями нужно аккуратно, поскольку объекты, чьи определения размещены последовательно, не всегда размещаются в памяти друг за другом. Язык С++ это не гарантирует.

Вопросы

1. Указатель – это объект, значением которого является _______________.

.

2. Выражение, позволяющее получить адрес объекта X:

1. $X

2. *X

3. &X

4. X*

5. #X

3. Укажите, какие арифметически операции определены над указателями

1. сложение указателей;

2. вычитание указателей;

3. сложение указателя и константы;

4. вычитание из указателя константы;

5. умножение указателей.

Лекция 13

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

В языке С++ указатели и массивы тесно связаны между собой.

По определению, имя массива является указателем – константой, значением которого является адрес первого элемента массива ( с индексом 0). Таким образом, для любого массива соблюдается правило:

<имя массива> = &<имя массива> = &<имя массива>[0]

Поскольку <имя массива> - указатель, то к нему можно применять арифметические операции. Тогда действие индексных скобок в записи

<имя массива>[<индекс>]

можно объяснить так:

*(<имя массива> + <индекс>)

Индекс элемента определяет смещение относительно начала массива.

Пример:

int z[3];

Тогда *(z+0) – это обращение к первому элементу z[0],

*(z+1) – это обращение ко второму элементу z[1],

*(z+2) – это обращение к третьему элементу z[2].

Необходимость использования круглых скобок в выражении *(z+i) обусловлена тем, что приоритет у операции *’ выше, чем у ‘+’.

Итак, в С++ предусмотрены 2 способа доступа к элементам массива: с помощью индексирования массивов и арифметики указателей.

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

Пример.

Вывести на экран символы заданной строки в столбик.

#include <iostream.h>

void main()

{ char X[ ] = “abcdef”;

int i = 0;

while ( *(X + i) != ‘\0’) // X[i] != ‘\0’

cout << “\n” << *(X+i++); // X[i++]

}

Результат:

a

b

c

d

e

f

Индексирование указателя.

В языке С++ указатель, который ссылается на массив, можно индексировать так, как если бы это было имя массива.

Рассмотрим пример.

#include <iostream.h>

void main()

{ char *p;

char X[80] =”abcdef”;

int i = 0;

p = X; // присваиваем указателю р адрес начала массива

while ( p[i] != ‘\0’)

cout << “\n” << p[i++];

}

При выполнении этой программы указатель p индексируется, что допустимо, поскольку выражение p[i] идентично по своему действию *(p+i).

Доступ к элементам многомерных массивов возможен также с помощью [ ] и с помощью указателей.

В общем случае переменная с индексами

Z[i][j][k]

соответствует выражению

*(*(*(z+i)+j)+k)

Указатели можно связывать с массивами как во время определения, так и в процессе выполнения программы.

Для этого существуют следующие возможности:

  1. <type>*<имя указателя> = <имя уже определенного массива типа type>;

  2. <type>*<имя указателя> = new <type>[ <размер массива>];

Например:

  1. int array[ ] = { 10,20,30,50 }; // массив

int *ptrarray = array ; // указатель связали с массивом

2) int *ptrarray = new int[4]; // указатель связали с выделенным участком памяти

Указатели и строки

В языке С++ строка представляется в виде одномерного символьного массива, последним элементом которого является нулевой байт.

Если для инициализации символьного массива используется строка

char array[ ] =”<инициализирующая строка>”;

то адрес первого символа строки становится значением указателя – константы array.

Если строка используется для инициализации указателя типа char*:

char *ptr = “<инициализируюшая строка>”;

то адрес первого элемента строки становится значением указателя ptr

Пример.

#include <iostream.h>

#include <string.h> // strlen()

void main()

{ char *array1 = “0123456789”; // массив символов инициализируется строкой

int k = strlen(array1) + 1;

char *array2 = new char[k];

for ( int i = 0; i < k; )

array2[i++] = *array1++;

cout << “array2=” << array2;

}

Результат:

0 1 2 3 4 5 6 7 8 9

В программе определен и инициализирован массив – строка array1. С помощью операции new выделена память для такого же по типу и размерам массива, связанного с указателем array2. Доступ к элементам массива, связанного с указателем array2 реализован с помощью [ ], а к элементам второго массива с помощью операции *. Указатель array1 меняется, перемещаясь по элементам массива, поэтому нельзя вывести array1 (будет пустая строка).

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

Вопросы

1.Имя массива

1.является указателем-константой;

2.является адресом первого элемента массива;

3.не связано с памятью компьютера.