- •Лабораторная работа № 12
- •Операторы для работы с указателями
- •Выражения с указателями
- •Присваивание указателей
- •Арифметические действия с указателями
- •Сравнение указателей
- •Указатели и массивы
- •Указатели на многомерные массивы
- •Создание передачи по ссылке
- •Передача массивов в функции
- •Методические указания
- •Варианты заданий
Присваивание указателей
Как и обычные переменные, указатели могут использоваться с правой стороны оператора присваивания для присвоения значения другому указателю. Например:
#include <stdio.h>
int main(void)
{
int x;
int *p1, *p2;
p1 = &x;
p2 = p1;
printf(“%p %p”, p1, p2); /* выводит адреса, хранящиеся в p1 и р2.
Они будут одинаковы */
return 0;
}
Арифметические действия с указателями
К указателям могут применяться только две арифметические операции: сложение и вычитание. Предположим, что p1 – это указатель на целое длиной 2 байта. После выполнения выражения
p1++;
значение указателя увеличится на 2, т. е. произойдет сдвиг указателя на 2 байта и он будет указывать на следующий элемент базового типа. Это справедливо и для уменьшения. Т. е. выражение
p1 ;
приведет к уменьшению значения указателя на 2 и сдвигу его к предыдущему элементу.
Можно вычитать из указателей любые целые числа. Выражение:
p1 = p1 + 7;
приводит к тому, что указатель указывает на седьмой элемент по сравнению с элементом, на который он указывал до присваивания.
Операция
р2 – р1
дает число элементов между указателями р1 и р2.
Сравнение указателей
В общем случае сравнение указателей с помощью одной из операций отношения имеет смысл в том случае, если оба указателя указывают на общий для них объект, например, массив или строку. Например:
#include <stdio.h>
int main(void)
{
int a, b;
int p1, *p2;
p1 = &a;
p2 = &b;
/* указатели р1 и р2 не равны, так как они указывают на разные объекты */
if (p1 != p2) printf(“Указатели р1 и р2 не равны\n”);
else printf(“Эта строка не напечатается”);
return 0;
}
Указатели и массивы
Между указателями и массивами существует прямая связь. Когда объявляется массив, например
int arr[25];
то этим определяется несколько больше, чем только выделение памяти для 25 элементов типа int. А именно, идентификатор массива arr определяется как указатель на первый (нулевой) элемент массива (адрес первого элемента массива &arr[0]). Так как идентификатор массива определяется как адрес, то следующее присваивание является допустимым:
int arr[25];
int *ptr;
ptr = arr;
Указатель ptr устанавливается на адрес первого элемента массива arr. Указатель ptr становится указателем на массив и его можно использовать для ссылки на элементы массива. Это присваивание можно записать в эквивалентной форме:
ptr = &arr[0];
Для доступа к элементам массива тоже существует два способа.
Первый способ доступа к элементам массива связан с использованием обычных индексных выражений вида arr[i]. Например, индексное выражение arr[5] ссылается на шестой элемент массива arr.
Второй способ доступа к элементам массива связан с использованием адресного выражения (или выражения с указателем на массив)
*(ptr+1) .
Например, адресное выражение *(ptr+5) ссылается на шестой элемент массива arr.
Рассмотрим как вычисляется индексное выражение arr[5]. Идентификатор arr является указателем начала массива. В соответствии с правилами выполнения операции сложения указателя с целой величиной целая величина преобразуется к адресному представлению путем умножения ее на размер типа, адресуемого указателем. Целая константа 5 умножается на размер типа int. Преобразованное значение числа 5 складывается со значением указателя arr, что дает адрес, относящийся к шестой позиции типа int после arr. К полученному адресу применяется операция «разадресация». Результатом является значение шестого элемента массива. В терминах языка С это выглядит так:
arr[5] = (arr+5) = (arr + 5*sizeof(int)) .
Следовательно, следующие выражения ссылаются на шестой элемент массива arr:
arr[5];
5[arr];
*(arr + 5);
*(5 + arr) .
Как следует из определения индексного выражения языка С, любой указатель может быть задан с индексом. Указатель на массив тоже может быть проиндексирован. Выражения
Ptr[5]
5[ptr]
ссылаются на тот же шестой элемент массива arr. Если расписать индексное выражение ptr[5], то получается адресное выражение
*(ptr + 5) .
В действительности компилятор языка С преобразует все индексные выражения в адресные выражения. Например:
arr[i] в *(arr + i)
ptr[i] в *(ptr + i) .
Таким образом, для доступа к i-му элементу массива arr можно пользоваться любым из данных выражений
arr[i] i[arr] *(arr + i)
*(i + arr) *(ptr + i) *(i + ptr)
ptr[i] i[ptr]
или другими словами, любое индексное выражение можно представить в виде выражения с указателем и смещением и наоборот. Причем обе формы доступа можно сочетать в одном операторе.
Пример.
/* Функция strcpy – копирование строк */
/* Вариант с использованием массивов */
char *strcpy (char s1[ ], char s2[ ])
{
int I;
for (i = 0; s1[i] = s2[i]; i++);
return (s1);
}
/* Вариант с использованием указателей */
char *strcpy (char *s1, char *s2)
{
char *str = s1;
while (*s1++ = *s2++);
return (str);
}
