
- •Часть 1
- •Лекция 1
- •Лекция 3
- •Алфавит
- •Идентификаторы
- •Константы
- •Знаки операций и разделители
- •4. Знаки операций.
- •Унарные операции.
- •Бинарные операции
- •5. Разделители.
- •Выражения.
- •4.1 Условный оператор
- •4.2. Переключатели
- •5.Оператор цикла с предусловием.
- •6.Оператор цикла с постусловием.
- •7.Оператор итерационного цикла
- •8.1.Оператор goto
- •8.2.Оператор return
- •8.3.Оператор break
- •8.4.Оператор continue
- •Функция strcpy()
- •2. Функция strcat().
- •3. Функция strcmp()
- •12. Функция strlen().
- •Лекция 11
- •Лекция 12
- •Лекция 14
Лекция 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)
Указатели можно связывать с массивами как во время определения, так и в процессе выполнения программы.
Для этого существуют следующие возможности:
<type>*<имя указателя> = <имя уже определенного массива типа type>;
<type>*<имя указателя> = new <type>[ <размер массива>];
Например:
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.не связано с памятью компьютера.