Указатель на элемент массива

Указатель на элемент массива

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

П усть есть массив
int A[5] = <1, 2, 3, 4, 5>;
Мы уже показали, что указатели очень похожи на массивы. В частности, массив хранит адрес, откуда начинаются его элементы. Используя указатель можно также получить доступ до элементов массива
int *p = A;
тогда вызов A[3] эквивалентен вызову *(p + 3)
На самом деле оператор [ ] является синтаксическим сахаром – он выполняет точно такую же работу. То есть вызов A[3] также эквивалентен вызову *(A + 3)

Тем не менее, важно понимать – указатели — это не массивы!

Это правильный код, который будет работать. Дело в том, что компилятор подменяет массив на указатель. Данный пример работает, потому что мы действительно работаем с указателем (хотя помним, что массив отличается от указателя). То же самое происходит и при вызове функции. Если функция требует указатель, то можно передавать в качестве аргумента массив, так как он будет подменён указателем.

В си существует одна занимательная особенность. Если A[i] это всего лишь синтаксический сахар, и A[i] == *(A + i) , то от смены слагаемых местами ничего не должно поменяться, т. е. A[i] == *(A + i) == *(i + A) == i[A] . Как бы странно это ни звучало, но это действительно так. Следующий код вполне валиден:

Многомерные массивы и указатели на многомерные массивы.

Т еперь рассмотрим такой пример

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

Мы получили указатель на первую строку. Далее вывели третий элемент. Либо так — создать массив указателей на строки

Только здесь уже p будет именем массива, каждый элемент которого является указателем. И точно так же, как мы обращались к элементам массива через массив указателей *p[3], через имя массива можно обратиться к элементу массива

Тоже самое правило действует и при вызове функций. Если функция требует указателя на указатель, то нельзя просто передать двумерный массив, потому что он не будет подменён указателем на указатель, а будет заменён массивом указателей.

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

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

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

Обновл. 31 Дек 2019 |

В C++ указатели и массивы тесно связаны между собой.

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

Для нас это массив из 4-ёх целых чисел, но для компилятора array является переменной типа int[4] . Мы знаем что array[0] = 5 , array[1] = 8 , array[2] = 6 и array[3] = 4 . Но какое значение имеет сам array ?

Переменная array содержит адрес первого элемента массива, как если бы это был указатель! Например:

Результат на моём компьютере:

The array has address: 004BF968
Element 0 has address: 004BF968

Обратите внимание, адрес, хранящийся в переменной array , является адресом первого элемента массива.

Распространённая ошибка думать, что переменная array и указатель на array являются одним и тем же объектом. Это не так. Хотя оба указывают на первый элемент массива, информация о типе данных у них разная. В примере выше, типом переменной array является int[4] , тогда как типом указателя на массив является int * .

Путаница в основном вызвана тем, что во многих случаях, при вычислении, фиксированный массив распадается (неявно преобразовывается) в указатель на первый элемент массива. Доступ к элементам по-прежнему осуществляется через указатель, но информация, полученная из типа массива (например, его размер), не может быть доступна из типа указателя.

Однако и это не является столь весомым аргументом, чтобы рассматривать фиксированные массивы и указатели как разные значения. Например, мы можем разыменовать массив, чтобы получить значение первого элемента:

Читайте также:  Программа для установки музыки на айфон

«Не в совокупности ищи единства, но в единообразии разделения».
Козьма Прутков.

Массив указателей как тип данных и как структура данных

Если это записать в терминах контекстного определения переменных, то получим, например

Многообразие вариантов реализации массивов указателей возникает по нескольким причинам:

· c ам массив указателей, указуемые переменные и ссылки (указатели) могут быть заданы статически (в тексте программы), либо динамически созданы во время ее выполнения;

· двоякая интерпретация указателя как указателя на отдельную переменную и на массив переменных (строку), позволяет создавать одномерные СД – массивы указателей на переменные и двумерные – массивы указателей на массивы (строки) таких переменных;

· указуемые переменные могут быть «собственностью» структуры данных, однако массив указателей может ссылаться и на переменные (объекты), являющиеся составными частями других структур данных.

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

Типы данных, используемые при работе с массивами указателей

Один тип данных уже был нами упомянут – это массив указателей, переменная вида int * p []. Кроме нее используется еще одни тип вида int ** pp , который можно определить в общем виде как указатель на указатель.

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

· традиционной интерпретации указателя как ссылки на отдельную переменную соответствует операция косвенного обращения по указателю * pp ;

· согласно концепции адресной арифметики любой указатель может ссылаться на неограниченный массив (память) с относительной адресацией от текущего положения указателя, что поддерживается операциями индексации pp [ i ] и добавления целого к указателю – p ++, p += n .

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

int a=5, b=10; int *p=&a; int **pp=&p;

Первый вариант – указатель на указатель на отдельную переменную, имеет довольно специфическое применение. Обычно от используется для передачи адреса заголовка какой-либо структуры данных при необходимости его изменения. В формальных параметрах для этой цели предпочтительнее использование ссылки на указатель (см. 6.3).

int a[10]=5, b[10]=10; int *p=a; int **pp=&p;


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

int a=5, b=10, с =15; int *p[]=<&a,&b,&c,NULL>; int **pp=p;

for (int s=0,i=0;i s=s+*pp[i];

Массив указателей на линейные массивы переменных является двумерной структурой данных и использует двойную индексацию. Функционально она является эквивалентом двумерного массива.

for (int j=0;j s=s+pp[i][j];

Статические и динамические массивы указателей

Промежуточные варианты массива указателей могут содержать как статические, так и динамические компоненты. В следующем примере статический массив указателей программно заполняется адресами элементов статического же массива (динамически формируются только сами указатели).

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

for (i=0; i *p = i; pd[i] = p; >

for (i=0; i // из 20 указателей типа int*

pp[i] = p; // можно pp[i]=new int; *pp[i]=i;

Массив указателей. Физический и логический порядок

При работе с массивом указателей используются два контекста:

//— Сортировка массива и массива указателей

void sort1 (double d[],int sz)<

void sort2 (double *pd[])<

for ( k=0, i=0; pd[i+1]!=NULL;i++)

if (* pd [ i ] > * pd [ i +1]) // Сравнение указуемых переменных

c = pd[i]; pd[i] = pd[i+1];pd[i+1] = c; k = 1; >

Динамический массив указателей на переменные (объекты)

Если динамический массив указателей ссылается на множество «уже известных», т.е. не им созданных и не ему принадлежащих переменных (объектов), то его уместно рассматривать как коллекцию, имеющую собственный логический порядок. В качестве примера рассмотрим функцию, которая создает массив указателей на упорядоченные по возрастанию положительные значения, взятые из массива. Как и для любого динамического массива, для массива указателей справедливы все выводы о его размерности: в данном случае она может быть вычислена заранее. Результат функции имеет тип double ** — указатель на динамический массив указателей, созданный внутри функции.

Читайте также:  Ссылка в эксель на другой файл

//——— Динамический массив указателей

// на упорядоченные положительные элементы исходного массива

double **create( double in[], int n)<

int i , j , m ; // Вычислить размерность

double **pp = new double *[m+1]; // Создать ДМУ

for (i=0,j=0; i // Запомнить указатель на

if (in[i]>0) pp[j++]=&in[i]; // положительный элемент

Динамический массив указателей на массивы переменных

//— Матрица произвольной размерности — массив указателей на массивы

double ** load ( char nm [], int & n , int & m )< // Размерности матрицы – по ссылке

if (fd==NULL) return NULL;

fscanf(fd,"%d%d",&n,&m); // Чтение размерностей

double **pp=new double*[n]; // Создание ДМУ по первой размерности

pp[i]=new double[m]; // Создание линейных ДМ (строк)

double sum(double **p , int n, int m )<

В данном представлении указуемые объекты – строки матрицы являются «собственностью» структуры данных. Процедура освобождения памяти из-под такой полностью динамической структуры данных происходит в два этапа: сначала в цикле уничтожаются динамические массивы – строки, а затем – сам массив указателей.

void destroy(double **pp,int n)<

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

// Простое однократное слияние — массив указателей

// на линейные массивы (части линейного массива)

vo > // любая сортировка одномерного массива

void big_sort(int A[], int N)<

int *L=new int[n],*C=new int[N]; // массив размерностей частей

L[n-1]=N-n*(n-1); // Размерность последнего массива

for (i=0; i // Сортировка частей

for (i=0; i // Слияние

for (k=-1, j=0; j // k — индекс строки с минимальным

if (L[j]==0) continue; // Пропуск слитых строк

C[i] = *B[k]; // Перенос элемента

B[k]++; // Сдвиг k- го указателя

for (i=0; i // Возвратить обратно

Представление текста. Динамический массив указателей на строки

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

char **pc, cc[100][80];

pc = new char *[101]; // Динамический массив указателей

for ( i =0; i i ++) pc [ i ] = cc [ i ]; // на строки статического массива

Здесь используются две особенности организации двумерных массивов. Во-первых, двумерный массив интерпретируется как массив элементов первого индекса, состоящих из элементов второго индекса, в данном случае -100 массивов символов по 80 символов в каждом. Во-вторых, идентификатор двумерного массива с одним индексом интерпретируется как указатель на начало соответствующего массива элементов второго индекса, в данном случае — указатель на i-й массив из 80 символов (строку).

Синтаксис операции извлечения символа из массива указателей на строки идентичен синтаксису двумерного массива символов, т.е. имеет место отмеченный выше функциональная идентичность массива указателей и двумерного массива. Первая индексация извлекает из массива i -ый указатель, вторая извлекает j -ый символ из строки, адресуемой указателем.

p[i] // указатель на i-ю строку в массиве указателей

A [ i ] // указатель на начало i -ой строки в двумерном массиве

p[i][j] // j-й символ в i-ой строке массива указателей

A [i][j] // j-й символ в i-ой строке двумерного массива

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

//— Массив указателей на отсортированные по длине слова

int my_strlen(char *p)<

char **SortedWords(char *p)<

int nw=0,k; char *q;

for (q=p; *q!=0; q++) // Подсчет количества слов по концам слов

char **qq=new char*[nw+1]; // Создать ДМУ на строки (символы строки)

if (*p!= ‘ ‘) qq[nw++]=p; // Строка начинается со слова

for (p++; *p!=0; p++) // Если начало слова —

if (p[0]!=’ ‘ && p[-1]==’ ‘) // запомнить текущий указатель в строке

k=0; // с использование собственной функции

for (int i=0; i // сравнения слов (до пробела)

char *g=qq[i]; qq[i]=qq[i+1]; qq[i+1]=g;

Проблема размерности динамического массива указателей

//——- Создание ДМУ из строк файла

char * *loadfile(FILE *fd)<

int n,sz=SIZE0; // Кол-во строк и размерность ДМУ

char **pp = new char*[sz]; // Создать ДМУ

for (n=0;fgets(str,1000,fd)!=NULL; n++)<

pp[n]=strdup(str); // Копия строки в ДМ

sz*=2; // удвоить размерность

Читайте также:  Максимальная температура северного моста gigabyte

pp[n] = NULL; // Ограничитель массива указателей

При вычислении размерности нового массива указателей в функции realloc используется размерность типа хранимого элемента – указателя sizeof(char*) , а возвращаемый адрес приводится к типу – указатель на указатель char **.

Лабораторный практикум

1. Функция получает линейный массив целых, находит в нем последовательности подряд возрастающих значений и возвращает их в динамическом массиве указателей на линейные массивы (аналог двумерного массива). В каждом из линейных динамических массивов содержится копия возрастающей последовательности, начиная с индекса 1, а под индексом 0 содержится его длина. Невозрастающие значения включаются в отдельный массив, добавляемый в конец (или начало) массива указателей.

2. Функция получает строку текста и возвращает динамический массив указателей на слова. Каждое слово копируется в отдельный массив в динамической памяти.

3. Функция получает строку, находит самый внутренний фрагмент в скобках и вырезает его. Операция повторяется до тех пор, пока не останется скобок. Полученные фрагменты и остаток строки вернуть в динамическом массиве указателей.

4. Функция находит в строке фрагменты, симметричные относительно центрального символа, длиной 7 и более символов (например, " abcdcba ") и возвращает динамический массив указателей на копии таких фрагментов.

5. Функция находит в строке пары фрагментов, содержащих последовательность одинаковых символов длиной более 3 (кроме пробела) и возвращает динамический массив указателей на копии таких фрагментов.

6. Стек моделируется при помощи динамического массива указателей на линейные массивы размерности N целых. Указатель стека – два индекса – в массиве указателей и линейном массиве. В операции push при переполнении текущего линейного массива в массив указателей добавляется новый, если операция pop переходит к предыдущему массиву, то текущий утилизуется.

7. Очередь моделируется при помощи динамического массива указателей на линейные массивы размерности N целых. Указатели на первый и последний элементы очереди – два индекса – в массиве указателей и линейном массиве. В операции добавления при переполнении текущего линейного массива в массив указателей добавляется новый, в операции извлечения – при переходе к следующему линейному массиву текущий утилизуется (указатели в массиве указателей смещаются к началу).

8. Функция читает из файла текст по словам и возвращает двухуровневый динамический массив указателей на строки, содержащие слова из исходного файла (тип данных char *** — см. 87. иерархические структуры данных). Размерность массива указателей нижнего уровня задана, каждый массив указателей ограничен NULL . Затем сортирует массивы указателей нижнего уровня, затем производит окончательную сортировкупутем однократного слияния (см. 4.6 Сортировка и поиск).

9. Функция читает из файла текст по словам и возвращает двухуровневый динамический массив указателей на строки на строки, содержащие слова из исходного файла, упорядоченные по алфавиту (тип данных char *** — см. 87. иерархические структуры данных). Размерность массива указателей нижнего уровня задана, каждый массив указателей ограничен NULL . Очередная строка вставляется с сохранением порядка, в первом цикле просматривается массив указателей первого уровня и в каждом – элемент с индексом 0 второго уровня. Если его значение больше нового, то выполянется вставка в предыдущий массив указателей нижнего уровня. Если при вставке происходит переполнение, массив создается новый массив указателей, в который копируется половина указателей текущего.

Вопросы без ответов

Содержательно сформулируйте действие, производимое функцией над массивом указателей. Напишите вызов функции для статических данных.

double * F(double *p[], int k) <

for ( int i=0; p[i]!=0; i++) ; // Текущая размерность массива указателей

if (k>=i) return NULL; // Больше текущей размерности — неудача

double *q=p[k]; // Запомнить k- ый указатель

for (; k // Сдвинуть " хвост" на 1 к началу — удалить

return q;> // k-ый и вернуть его

for (int i=0; pp[i]!=NULL;i++) printf(" %2.0lf",*pp[i]);

Функция возвращает указатель на double . Поскольку она получает массив указателей, можно предположить, что он берется оттуда. Действительно, из массива копируется указатель, номер которого задан формальным параметром. То есть функция возвращает указатель по заданному логическому номеру. Первоначально подсчитывается текущая размерность структуры данных – количество указателей в массиве. Если логический номер его превышает, возвращается NULL . И последнее. После запоминания k -го указателя все последующие указатели сдвигаются на 1 к началу, таким образом, выделенный указатель «затирается». То есть функция исключает указатель по логическому номеру и возвращает его в качестве результата. Для задания статической структуры данных сначала определяются указуемые переменные типа double , а замет массив указателей инициализируется их адресами.

Ссылка на основную публикацию
Тор браузер андроид 4pda
Браузер Тор доступен не только для компьютеров и ноутбуков под управлением различных операционных систем. Разработчики обеспокоились и его выпуском для...
Телефон греется и тормозит что делать
Почему тормозит устройство на Andro >Прежде чем перейти непосредственно к решению проблем, стоит указать на их причины. Зная о том,...
Телефон завис на загрузке андроид
В результате поломки аппаратной части или сбоя в работе ОС любой Android-смартфон может перестать реагировать на кнопку включения. Частой можно...
Тор браузер без установки
Tor Browser (ранее он назывался Tor Browser Bundle) – наиболее защищенный интернет-обозреватель из представленных в настоящий момент. Ввиду высокой популярности...
Adblock detector