Указатель - это переменная, содержащая адрес другой переменной. Указатели очень широко используются в языке С++. Это происходит отчасти потому, что иногда они дают единственную возможность выразить нужное действие, а отчасти потому, что они обычно ведут к более компактным и эффективным программам, чем те, которые могут быть написаны другими способами. Так как указатель содержит адрес объекта, это дает возможность "косвенного" доступа к этому объекту через указатель.
Пример создания указателя на int и на double:
int a=5; int *pa = &a; double d = 1.5; double *pd = &d; pa = pd //нельзя! т.к. указатели на данные разного типа pa = (int*)pd; //компилируется
Последняя операция лишена смысла, т.к. элемент типа double занимает 8 байт, а элемент типа int - 4 байта (в х32). Поэтому указатель pa будет указывать на первые 4 байта данных, расположенных по адресу, хранящемуся в pd.
Рассмотрим пример функции, которая должна менять местами два значения:
void swap (int a, int b) { int t = a; a = b; b = t; }
Вызов swap:
int k=10, m=20; swap (k, m);Несмотря на то, что все написано правильно, после возврата из функции swap значения k и m не изменились. Это обусловлено тем, что при передаче в функцию происходит копирование параметров. Таким образом, изменились скопированные значения, а исходные остались прежними. Чтобы иметь возможность менять параметры их следует передавать по указателю:
void swap (int *a, int *b)
{
int t = *a;
*a = *b;
*b = *t;
}
Вызов swap:
int k=10, m=20;
swap (&k, &m);
Теперь значения k и m изменились, так как при передаче в функцию произошло копирование адресов переменных, а адреса мы менять не хотели.
Опр. Массив - это непрерывная область памяти, в которой расположены однотипные элементы.
int a[5] = {1, 2, 3}; //в памяти лежат 1,2,3,0,0 _ _ _ ___________________ _ _ _ _ _ |_1_|_2_|_3_|_0_|_0_|_ _
Связь массивов и указателей: имя встроенного массива преобразуется в указатель (на первый элемент массива):
int *p = 0; //"нулевой" указатель, nil. Означает, что он не указывает ни на какую ячейку памяти. p = a; // &a[0] a = p; // обратное (р = а) неверно
Передача массива в функцию в качестве параметра:
int max_element (int m[10]) {}
Массив передается по ссылке (не копируется). Вызов: max_element(a); Функция max_element будет работать только с массивами из 10 элементов (с другими - не скомпилируется). Правильнее делать так:
int max_element (int *m, int size) { int max = *m; for (int i=1; i < size; ++i) if (m[i] < max) max = m[i]; return max; }
Вызов: max_element(a, 5);
Проблемы такой реализации:
Пусть q, p - указатели, а i - число. Возможные операции:
Рассмотрим две реализации функции подсчета длины символов в строке:
int strlen1 (char *s) { int len=0; while (s[len]!=0) ++len; return len; } int strlen2 (char *s) { char *p = s; while (*p!=0) ++p; return p-s; }
Первая реализация более трудоемка, так как s[len] <=> *(s+len) то есть смещению по массиву и разименованию (2 операции), в то время, как во второй реализации мы работаем с указателями напрямую. Но второй вариант менее читабелен. Второй способ (работу с указателями напрямую) следует использовать и при передаче массива в функцию.
Рекомендуется следующая сигнатура:
int foo (int *first, int *last);
Где first является указателем на первый элемент массивва (a[0]), а last указателем на область памяти сразу за массивом ("a[n]"). Такой выбор указателя last обеспечивает работу с массивами нулевой длины.
Перепишем функцию max_element:
int* max_element (int *m, int *last) { int *max = m; for (; m != last; ++m) if (*m > *max) max = m; return max; }
Вызов: max_element(a, a+5);
Следует отметить, что теперь функция max_element возвращает не значение максимального элемента массива, а указатель на него.
int findch (char *s, char p) { for (int i = 0; s[i] != 0; ++i) if (s[i] == p) return i; return -1; }Вообще данная функция должна информировать о двух вещах: во-первых, найден ли искомый символ, а во-вторых, если да, то его позицию. Будет правильно возвращать 2 значения соответственно, а не совмещать все в одном. Перепишем функцию:
bool findch (char *s, char p, int *n=0) { for (int i = 0; s[i] != 0; ++i) if (s[i] == p) { *n = i; return true; } return false; }
Вызов:
int idx = 0; if (findch(s, ' ', &idx)) {...}
Чтобы возвращать не индекс элемента, а его позицию в массиве используют указатель на указатель:
bool findch (char *s, char p, char **pos=0) { while (*s!=0) { if (*s==p) { if (pos) *pos=s; return true; } ++s; } return false; }
Вызов:
char *pos = 0; if (findch(s, ' ', &pos) {...}
Работа с указательями напрямую синтаксически неудобна. Для достижения удобства используют ссылки:
int a = 5; int &b = a; //b - ссылка на а b = 10; //в а тоже стало 10
Функция swap с использованием ссылок будет выглядеть так:
void swap (int &a, int &b) { int t=b; b=a; a=t; }
Вызов:
int c = 30; swap (a, c);
При передаче параметров в функцию по ссылке копирования данных не происходит.
Замечание: