Указатели и массивы.
В языка С между указателями и массивами существует тесная связь. Имя массива – это не что иное, как указатель на первый элемент массива – элемент с индексом 0. когда объявляется массив в виде int array[25], то компилятор не только выделяет память для 25 элементов массива типа int, но и создает указатель с именем array, который ссылается на первый по счету элемент массива (с нулевым индексом). Сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array. Следует отметить, что указатель array является константным, значение которого можно использовать в выражениях, но изменить нельзя. Пример 10. имя массива можно приравнять указателю, т.к. оно также является указателем. … int array[25]; int *ptr; ptr=array; … Здесь указатель ptr устанавливается на начало массива array, причем присваивание ptr= array можно записать в эквивалентной форме: ptr=& array[0]. Для доступа к элементам массива существует два различных способа. Первый способ связан с использованием обычных индексных выражений в [ ], например, инструкция array[10]=35 записывает константу 35 в элемент с индексом 10, а инструкция array[i+4]=rev записывает значение переменной rev в (i+4)-й элемент массива array. Второй способ доступа к элементам массива связан с применением указателей: *(array+10)=35; *(array+i+4) = 7. Рассматривая имя массива как указатель, мы проделали те же действия, что и в предыдущем примере. Операция * в данном случае – это «разыменование» или обращение к объекту через указатель. Подобным же образом можно обращаться к элементам массива с помощью независимого (переменного) указателя. Пример 11. Сравнение двух способов доступа к элементам массива. // определение массива mas и указателя q char mas[100], q; //инициализация указателя – установка на нулевой элемент массива q=mas; //доступ к элементам массива (следующие записи эквивалентны) mas[0]; //*q mas [10]; //*(q+10) mas[n-1]; //*(q+n-1)
Использование индексации для доступа к массиву более наглядно, но использование указателя более эффективно. При индексировании элемента массива его адрес каждый раз вычисляется по начальному адресу (имени массива) и смещению (индексу), что снижает эффективность полезных вычислений. Поскольку быстродействие программы – одно из важнейших ее свойств, предпочтительнее использовать указатели-переменные. Пример 12. Обработка элементов массива. … char s[20]; //определение массива типа char //определение и установка указателя на 0-й элемента массива char *p=&s[0]; //аналог *p=s; *(p+10)=’m’; //записываем символ ‘m’ в 10-й элемент р++; //установка указателя на 1-ый элемент *(р+10)=’k’; //меняем значение 11-го элемента if (*(p-1)!= ‘\n’) //проверка 0-го элемента … В примере массив и указатель имеют тип char (размер элемента 1 байт). Прибавляем к адресу 1, мы сдвигаемся на один байт. А как быть, если мы работаем, например, с типом int, у которого размер 4 байта? Оказывается, что и в этом случае все показанные выше арифметические операции с указателями дадут правильный результат. Разумеется, к адресу будет добавляться не 1, а 4, но нам об этом даже не придется думать. Пример 13. … int s[20]; //определение массива типа int //определение и установка указателя на 0-й элемента массива int *p=&s[0]; //аналог *p=s; *(p+10)=1; //записываем 1 в 10-й элемент р++; //установка указателя на 1-ый элемент *(р+10)=2; //меняем значение 11-го элемента if (*(p-1)!= 10) //проверка 0-го элемента … Пример, приведенный ниже, отражает на экране абсолютные значения адресов элементов массивов различных типов, что позволяет наглядно увидеть особенности арифметики указателей.
Пример 14. вывести на экран элементы массива и их адреса #include <stdio.h> void main() { char s[] =”string”, *p; //работа с массивом типа char for (p=s; *p!=0; p++) //p+1 в абсолютных значениях printf(“s: %c \t p = %p\n”, *p, p); int q[] ={10, 20, 30, 40, 50, 0}, *p1; //работа с массивом типа int for (p1=q; *p1!=0; p1++) //p1+4 в абсолютных значениях printf(“q[0]= %d \t p1 = %p \n”, *p1, p1); }
Для вывода значения указателя (адреса элемента массива) на экран дисплея в функции printf() используется спецификатор формата %р (тип указатель). Итак, мы разобрали одно из назначений указателей – обращение к элементам массивов (или других сложных типов данных). Другое, не менее важное применение указателей – передача параметров и возврат значений из функций.
|