Студопедия

Главная страница Случайная страница

Разделы сайта

АвтомобилиАстрономияБиологияГеографияДом и садДругие языкиДругоеИнформатикаИсторияКультураЛитератураЛогикаМатематикаМедицинаМеталлургияМеханикаОбразованиеОхрана трудаПедагогикаПолитикаПравоПсихологияРелигияРиторикаСоциологияСпортСтроительствоТехнологияТуризмФизикаФилософияФинансыХимияЧерчениеЭкологияЭкономикаЭлектроника






Связь между указателями и массивами






Одной из наиболее распространенных конструкций с использованием указателей являются массивы. Результатом использования указателей для массивов является меньшее количество используемой памяти и высокая производительность.

В языке С массивы – это упорядоченные данные (элементы) одного типа. Компилятор языка С рассматривает имя массива как адрес его первого элемента (в языке С нумерация элементов массива начинается с нуля). Например, если имя массива Arr с десятью элементами, то i -й элемент (0 ≤ i < 10) компилятор преобразует его по правилам работы с указателями с операцией разыменования: *(Arr + i). Здесь Arr как бы указатель, а i – целочисленная переменная. Сумма (Arr + i) указывает на i -й элемент массива, а операция разыменования (оператор раскрытия ссылки *) дает значение самого элемента, т.е. Arr[i] идентично *(Arr+i).

Имя массива без индекса образует указатель на начало этого массива.

Имеется одно различие между именем массива и указателем, которое необходимо иметь в виду. Указатель является переменной, так что операции ptr1=Arr и ptr1++ имеют смысл. Но имя массива является константой, а не переменной: конструкции типа Arr=ptr1 или Arr++, или Arr=& x будут незаконными.

Т.е. имя массива всегда указывает на одно и то же место в памяти – на нулевой элемент.

Пусть, например, массив Arr содержит 10 целочисленных переменных:

int Arr[10];

Тогда можно объявить указатель ptr, который будет указывать на элементы массива Arr:

int *ptr;

Тип указателя (в примере это int) должен соответствовать типу объявленного массива.

Для того, чтобы указатель ptr ссылался на первый элемент (с нулевым индексом) массива Arr, можно использовать утверждение:

ptr = Arr;

В то же время можно использовать прямую адресацию:

ptr = & Arr[0];

Обе формы записи эквивалентны.

Аналогичные утверждения будут справедливы для других типов массивов: char, float, double и пр.

Если указатель ptr указывал на первый элемент (с нулевым индексом) массива Arr, то для обращения к следующему элементу массива допустимы следующие формы утверждений:

ptr = & Arr[1];

ptr += 1;

Соответственно, выражение *(ptr+1) будет ссылаться на значение, содержащееся в элементе Arr[1].

Утверждение

ptr += n;

заставит указатель *ptr ссылаться на элемент массива, находящийся на расстоянии n от того, на который ранее ссылался указатель, независимо от типа элементов, содержащихся в массиве. Разумеется, значение n должно быть в допустимых пределах для данного объема массива.

При работе с указателями и массивами особенно удобны операторы инкремента " ++" и декремента " ––". Использование оператора инкремента с указателем аналогично операции суммирования с единицей, а операция декремента имеет тот же эффект, что и вычитание единицы из указателя.

В языке программирования С вполне корректной операцией является сравнение указателей. К указателям применяются операции сравнения " > ", " > =", "! =", " ==", " < =", " < ". Сравнивать указатели допустимо только с другими указателями того же типа или с константой NULL, обозначающей значение условного нулевого адреса. Константа NULL – это особое значение переменной-указателя, присваиваемое ей в том случае, когда она не должна иметь никакого значения. Его можно присвоить переменной-указателю любого типа. Оно представляет собой целое число нуль.

Особое значение имеет сравнение двух указателей, которые связаны с одним и тем же массивом данных.

Рассмотрим инициализацию указателей типа char:

char *ptr = " hello, world";

Переменная *ptr является указателем, а не массивом. Поэтому строковая константа " hello, world" не может храниться в указателе *ptr. Тогда возникает вопрос, где хранится строковая константа. Для этого следует знать, что происходит, когда компилятор встречает строковую константу. Компилятор создает так называемую таблицу строк. В ней он сохраняет строковые константы, которые встречаются ему по ходу чтения текста программы. Следовательно, когда встречается объявление с инициализацией, то компилятор сохраняет " hello, world" в таблице строк, а указатель *ptr записывает ее адрес.

Поскольку указатели сами по себе являются переменными, их можно хранить в массивах, как и переменные других типов. Получается массив указателей.

Массив указателей фиксированных размеров вводится одним из следующих определений:

тип *имя_массива [размер];

тип *имя_массива [ ] = инициализатор;

тип *имя_массива [размер] = инициализатор;

В данной инструкции тип может быть как одним из базовых типов, так и производным типом;

имя_массива – идентификатор, определяемый пользователем по правилам языка С;

размер – константное выражение, вычисляемое в процессе трансляции программы;

инициализатор – список в фигурных скобках значений элементов заданного типа (т.е. тип).

Рассмотрим примеры:

int data[7]; // обычный массив

int *pd[7]; // массив указателей

int *pi[ ] = { & data[0], & data[4], & data[2] };

В приведенных примерах каждый элемент массивов pd и pi является указателем на объекты типа int.

Значением каждого элемента pd[j] и pi[k] может быть адрес объекта типа int. Все 6 элементов массива pd указателей не инициализированы.В массиве pi три элемента, и они инициализированы адресами конкретных элементов массива data.

В случае обработки строк текста они, как правило, имеют различную длину, и их нельзя сравнить или переместить одной элементарной операцией в отличие от целых чисел. В этом случае эффективным средством является массив указателей. Например, если сортируемые строки располагаются в одном длинном символьном массиве вплотную — начало одной к концу другой, то к каждой строке можно обращаться по указателю на ее первый символ. Сами же указатели можно поместить в массив, т.е. создать массив указателей. Две строки можно сравнить, рассмотрев указатели на них.

Массивы указателей часто используются при работе со строками. Можно привести пример массива строк о студенте, задаваемый с помощью массива указателей.

char *ptr[ ] = {

" Surname", //Фамилия

" Name", // Имя

" group", // группа

" ACOUY" // специальность

};

С помощью массива указателей можно инициализировать строки различной длины. Каждый из указателей массива указателей указывает на одномерный массив символов (строку) независимо от других указателей.

В языке программирования С предусматриваются ситуации, когда указатели указывают на указатели. Такие ситуации называются многоуровневой адресацией. Пример объявления указателя на указатель:

int **ptr2;

В приведенном объявлении **ptr2 – это указатель на указатель на число типа int. При этом наличие двух звездочек свидетельствует о том, что имеется двухуровневая адресация. Для получения значения конкретного числа следует выполнить следующие действия:

int x = 88, *ptr, **ptr2;

ptr = & x;

ptr2 = & ptr;

printf(" %d", **ptr2);

В результате в выходной поток (на дисплей пользователя) будет выведено число 88. В приведенном фрагменте переменная *ptr объявлена как указатель на целое число, а **ptr2 – как указатель на указатель на целое. Значение, выводимое в выходной поток (число 88), осуществляется операцией разыменования указателя **ptr2.

Для многомерных массивов указатели указывают на адреса элементов массива построчно. Рассмотрим пример двухмерного целочисленного массива М размера 3 5, т.е. состоящего из 3 строк и 5 столбцов, и определим указатель:

int M[3][5]= {{1, 2, 3, 4, 5}, {–6, –7, –8, –9, –10}, {11, 12, 13, 14, 15}};

int *ptr;

Элементы массива (по индексам) располагаются в ячейках памяти по строкам в следующем порядке:

M[0][0], M[0][1], M[0][2], M[0][3], M[0][4], M[1][0], M[1][1], M[1][2], M[1][3], M[1][4], M[2][0], M[2][1], M[2][2], M[2][3], M[2][4].

Сначала запоминается первая строка, затем вторая, затем третья. В данном случае двухмерный массив – это массив трех одномерных массивов, состоящих из 5 элементов.

Указатель указывает на адреса элементов в порядке расположения их в памяти. Поэтому тождественны равенства:

ptr == & M[0][0]; //? 1-я строка, 1-й столбец

ptr + 1 == & M[0][1]; // 1-я строка, 2-й столбец

ptr + 2 == & M[0][2]; // 1-я строка, 3-й столбец

ptr + 3 == & M[0][3]; // 1-я строка, 4-й столбец

ptr + 4 == & M[0][4]; // 1-я строка, 5-й столбец

ptr + 5 == & M[1][0]; // 2-я строка, 1-й столбец

ptr + 6 == & M[1][1]; // 2-я строка, 2-й столбец

ptr + 7 == & M[1][2]; // 2-я строка, 3-й столбец

ptr + 8 == & M[1][3]; // 2-я строка, 4-й столбец

ptr + 9 == & M[1][4]; // 2-я строка, 5-й столбец

ptr + 10 == & M[2][0]; // 3-я строка, 1-й столбец

ptr + 11 == & M[2][1]; // 3-я строка, 2-й столбец

ptr + 12 == & M[2][2]; // 3-я строка, 3-й столбец

ptr + 13 == & M[2][3]; // 3-я строка, 4-й столбец

ptr + 14 == & M[2][4]; // 3-я строка, 5-й столбец

Практически следует произвести инициализацию указателя, например, взяв адрес первого элемента матрицы, а затем – обращение к элементам матрицы, можно производить через указатель:

ptr = & M[0][0];

*(ptr + i*n + j);

где i – номер строки заданной матрицы, j – номер столбца, n – число столбцов в матрице.






© 2023 :: MyLektsii.ru :: Мои Лекции
Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав.
Копирование текстов разрешено только с указанием индексируемой ссылки на источник.