Указатели языка С. Указатели и массивы. Константные указатели и указатели на константу

Страницы работы

Фрагмент текста работы

функции (автоматические переменные), не инициализированы вовсе. Использование такого указателя до присвоения ему осмысленного значения является серьезной ошибкой. Нулевой адрес (NULL или просто 0) не может быть присвоен указателю никакой из функций или операций динамического выделения памяти (new, malloc). В связи с этим он служит признаком того, что указатель еще не был инициализирован программистом.

NULL—это символическая константа, определенная в stdio.h для нулевого указателя. Символической константой называется константа, определенная с помощью макроподстановки #define. Например,

#define NULL (void*)0

определяет символическую константу NULL, которая на первом этапе компиляции (фаза препроцессора) будет заменена препроцессором на выражение (void*)0. Таким образом, константа NULL—это целый ноль, явно приведенный к типу void*, то есть указателю на произвольный, неопределенный тип.

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

При объявлении указателя задается тип переменных, на которые он может указывать. Это кажется лишним, так как указатель любого типа в Win32 — 4 байта, содержащие адрес какого-либо объекта в памяти. Но все дело в том, что с указателями связана адресная арифметика, правила которой различны для разных типов указателей. Так, если к указателю на тип int прибавить единицу, то его значение изменится на 4 байта или sizeof(int). Это же действие в Win16 приведет к увеличению указателя на 2 байта.

Забегая вперед, скажем, что при увеличении на единицу указателя, содержащего адрес массива объектов какого-либо класса (например, класса Man), указатель сдвинется в памяти ровно на один объект этого класса, независимо от того, сколько памяти он занимает.

Имя массива в языке С фактически является указателем на первый его элемент. Первый элемент соответствует нулевому значению индекса (или индексов в случае многомерных массивов). Если объявлен массив float a[16];, то справедливо равенство а==&a[0]. Переменные типа указатель могут быть использованы и часто используются для доступа к элементам массива. Рассмотрим пример, в котором совместно используются массив переменных вещественного типа и указатель на переменные этого же типа. Имеют смысл следующие присвоения:

float a[16],*p;       // Объявление массива и указателя

for (int i=0;  i<16;  i++)

  a[i] = float(i*i);   // Заполнение массива

p = &a[6];    // p указывает на а[6]

*p = 3.14f;   // Равносильно a[6] = 3.14;

p++;          // Теперь p указывает на а[7]. Произошел сдвиг на 4 байта

a[1] = *(p+3); // Равносильно a[1] = a[10]; В a[1] попадает 100.

a[2] = ++*p;  // Равносильно a[2] = ++a[7]; В a[2] попадает 50.

a[3] = *++p;  // Равносильно a[3] = a[8]; В a[3] попадает 64.

¨  Адресная арифметика, например p+3 или ++p, осуществляется в единицах объявленного базового типа данных float. Если в конкретной вычислительной системе число типа float занимает 4 байта, то результатом операции р+3 будет адрес, отстоящий от р на 12 байт.

¨  Значение ++*p вычисляется так: сначала выбирается (*p) — содержимое по адресу p, то есть a[7], так как в данный момент указатель содержит &a[7]. Затем выполняется приращение (increment) a[7], то есть увеличение a[7] на единицу.

¨  При вычислении *++p, наоборот, сначала производится изменение указателя (++p), потом выборка содержимого по адресу, на который он указывает.

Тесную связь между указателями и массивами проиллюстрируем еще одним примером, в котором суммируются члены сходящегося ряда, предварительно размещенные в динамическом массиве. Так как для вычисления квадрата мы пользуемся функцией pow(a,b), позволяющей возвести a в степень b, то следует подключить файл math.h, в котором определены математические функции.

double Sum (float*, float*);    // Прототип функции

void main()

{                        // Сумма сходящегося ряда

int size;

puts ("\n Enter an array size:");

size = in (1, 100000);         // Наша функция ввода

float *first = new float[size]; // Динамический массив

float *last = first + size - 1; // Адрес последнего элемента

printf ("\n\t Starting address of the array = %p"

    "\n\t Ending address of the array   = %p", first,last);

for (int i=0; i<size; i++)   // Заполнение массива (члены ряда)

  first[i] = 1./pow(i+1, 2);

printf ("\n\t Current sum value = %8.4f", Sum (first, last));

}

      //===== Вычисление суммы элементов массива =====//

double Sum (float *start, float *end)

{

double sum=0.;

while (start <= end)  // Пробег по массиву с помощью указателей

  sum += *start++;     // Вклады в сумму

return sum;

}

Указатели first и last содержат адреса начала и конца массива вещественных чисел длиной в size элементов. Массив заполняется в цикле for. Сумма его элементов, вычисленная функцией Sum, возвращается в точку вызова и непосредственно выводится функцией printf. Прототип функции Sum дан заранее, так как сама функция следует за главной процедурой.

Указатели start и end, действующие в функции Sum, получают значения

Похожие материалы

Информация о работе