Как обратиться к элементу массива через указатель
Перейти к содержимому

Как обратиться к элементу массива через указатель

  • автор:

Как обратиться к элементу массива через указатель

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

Имя массива без индексов в Си является адресом его первого элемента. Соответственно через операцию разыменования мы можем получить значение по этому адресу:

#include int main(void) < int array[] = ; printf("array[0] = %d", *array); // array[0] = 1 return 0; >

Мы можем пробежаться по всем элементом массива, прибавляя к адресу определенное число:

#include int main(void) < int array[5] = ; for(int i = 0; i < 5; i++) < void* address = array + i; // получаем адрес i-го элемента массива int value = *(array + i); // получаем значение i-го элемента массива printf("array[%d]: address=%p \t value=%d \n", i, address, value); >return 0; >

То есть, например, адрес второго элемента будет представлять выражение a+1 , а его значение — *(a+1) .

Со сложением и вычитанием здесь действуют те же правила, что и в операциях с указателями. Добавление единицы означает прибавление к адресу значения, которое равно размеру типа массива. Так, в данном случае массив представляет тип int , размер которого, как правило, составляет 4 байта, поэтому прибавление единицы к адресу означает увеличение адреса на 4. Прибавляя к адресу 2, мы увеличиваем значение адреса на 4 * 2 =8. И так далее.

В итоге в моем случае я получу следующий результат работы программы:

array[0]: address=0060FE98 value=1 array[1]: address=0060FE9C value=2 array[2]: address=0060FEA0 value=3 array[3]: address=0060FEA4 value=4 array[4]: address=0060FEA8 value=5

В то же время имя массива это не стандартный указатель, мы не можем изменить его адрес, например, так:

int array[5] = ; array++; // так сделать нельзя int b = 8; array = &b; // так тоже сделать нельзя

Использование указателя для работы с массивом

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

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array printf("value: %d \n", *ptr); // 1 return 0; >

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

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array ptr = ptr + 2; // перемезаем указатель на 2 элемента вперед printf("value: %d \n", *ptr); // value: 3 return 0; >

Здесь указатель ptr изначально указывает на первый элемент массива. Увеличив указатель на 2, мы пропустим 2 элемента в массиве и перейдем к элементу array[2] .

И как и другие данные, можно по указателю изменить значение элемента массива:

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array ptr = ptr + 2; // переходим к третьему элементу *ptr = 8; // меняем значение элемента, на который указывает указатель printf("array[2]: %d \n", array[2]); // array[2] : 8 return 0; >

Стоит отметить, что указатель также может использовать индексы, как и массивы:

#include int main(void) < int array[5] = ; int *ptr = array; // указатель ptr хранит адрес первого элемента массива array int value = ptr[2]; // используем индексы - получаем 3-й элемент (элемент с индексом 2) printf("value: %d \n", value); // value: 3 return 0; >

Строки и указатели

Ранее мы рассмотрели, что строка по сути является набором символов, окончанием которого служит нулевой символ ‘\0’. И фактически строку можно представить в виде массива:

char hello[] = "Hello METANIT.COM!";

Но в языке Си также для представления строк можно использовать указатели на тип char :

#include int main(void) < char *hello = "Hello METANIT.COM!"; // указатель на char - фактически строка printf("%s", hello); return 0; >

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

Перебор массива с помощью указателей

С помощью указателей легко перебрать массив:

int array[5] = ; for(int *ptr=array; ptr

Так как указатель хранит адрес, то мы можем продолжать цикл, пока адрес в указателе не станет равным адресу последнего элемента ( ptr

Аналогичным образом можно перебрать и многомерный массив:

#include int main(void) < int array[3][4] = < , , >; int n = sizeof(array)/sizeof(array[0]); // число строк int m = sizeof(array[0])/sizeof(array[0][0]); // число столбцов int *final = array[0] + n * m - 1; // указатель на самый последний элемент for(int *ptr=array[0], i = 1; ptr > return 0; >

Так как в данном случае мы имеем дело с двухмерным массивом, то адресом первого элемента будет выражение array[0] . Соответственно указатель указывает на этот элемент. С каждой итерацией указатель увеличивается на единицу, пока его значение не станет равным адресу последнего элемента, который хранится в указателе final.

Мы также могли бы обойтись и без указателя на последний элемент, проверяя значение счетчика, пока оно не станет равно общему количеству элементов (m * n):

for(int *ptr = array[0], i = 0; i < m*n;) < printf("%d \t", *ptr++); if(++i%m==0) < printf("\n"); >>

Но в любом случае программа вывела бы следующий результат:

1 2 3 4 5 6 7 8 9 10 11 12

Как обратиться к элементу массива через указатель

В C++ указатели и массивы тесно связаны. Обычно компилятор преобразует массив в указатели. С помощью указателей можно манипулировать элементами массива, как и с помощью индексов.

Имя массива по сути является адресом его первого элемента. Соответственно через операцию разыменования мы можем получить значение по этому адресу:

#include int main() < int nums[] ; std::cout

Так, в моем случае я получу следующий консольный вывод:

nums[0] address: 0x1f1ebffe60 nums[0] value: 1

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

#include int main() < int nums[] ; int num2 = *(nums + 1); // второй элемент int num3 = *(nums + 2); // третий элемент std::cout int, размер которого, как правило, составляет 4 байта, поэтому прибавление единицы к адресу означает увеличение адреса на 4. Прибавляя к адресу 2, мы увеличиваем значение адреса на 4 * 2 = 8. И так далее.

Например, в цикле пробежимся по всем элементам:

#include int main() < int nums[] ; for(unsigned i<>; i < std::size(nums); i++) < std::cout nums[0]: address=0xd95adffc30 value=1 nums[1]: address=0xd95adffc34 value=2 nums[2]: address=0xd95adffc38 value=3 nums[3]: address=0xd95adffc3c value=4 nums[4]: address=0xd95adffc40 value=5

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

int nums[] ; nums++; // так сделать нельзя int b ; nums = &b; // так тоже сделать нельзя

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

Имя массива всегда хранит адрес самого первого элемента. И нередко для перемещения по элементам массива используются отдельные указатели:

int nums[] ; int *ptr ; int num3 = *(ptr+2); std::cout 

Здесь указатель ptr изначально указывает на первый элемент массива. Увеличив указатель на 2, мы пропустим 2 элемента в массиве и перейдем к элементу nums[2] .

Можно сразу присвоить указателю адрес конкретного элемента массива:

int nums[] ; int *ptr ; // адрес третьего элемента std::cout #include int main() < const int n = 5; int nums[n]; for(int *ptr; ptr <=&nums[n-1]; ptr++) < std::cout << "address=" << ptr << "\tvalue brush:cpp;">#include int main() < int nums[3][4] < , , >; unsigned int n < sizeof(nums)/sizeof(nums[0]) >; // число строк unsigned int m < sizeof(nums[0])/sizeof(nums[0][0]) >; // число столбцов int *end ; // указатель на самый последний элемент 0 + 3 * 4 - 1 = 11 int *ptr ; // указатель на первый элемент for( unsigned i; ptr > >

Поскольку в данном случае мы имеем дело с двухмерным массивом, то адресом первого элемента будет выражение a[0] . Соответственно указатель указывает на этот элемент. С каждой итерацией указатель увеличивается на единицу, пока его значение не станет равным адресу последнего элемента, который хранится в указателе end.

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

#include int main() < const unsigned n ; // число строк const unsigned m ; // число столбцов int nums[n][m] < , , >; const unsigned count ; // общее количество элементов int *ptr; // указатель на первый элемент первого массива for(unsigned i; i > >

Но в обоих случаях программа вывела бы следующий результат:

1 2 3 4 5 6 7 8 9 10 11 12

Указатель на строки и массивы символов

Поскольку массив символов может интерпретироваться как строка, то указатель на значения типа char тоже может интерпретироваться как строка:

#include int main() < char hello[] ; char *phello ; std::cout 

При выводе на консоль значения указателя фактически будет выводиться строка.

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

std::cout 

Если же необходимо вывести на консоль адрес указателя, то его надо преобразовать к типу void*:

std::cout 

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

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

char *phello ;

Однако следует учитывать, что строковые литералы в С++ рассматриваются как константы. Поэтому предыдущее определение указателя может при компиляции вызвать как минимум предупреждение, а попытка изменить элементы строки через указатель - к ошибке компиляции. Поэтому при определении указателя на строку, следует определять указатель как указатель на константу:

#include int main() < const char *phello ; // указатель на константу std::cout 

Массивы указателей

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

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

#include int main() < char langs[][20] < "C++", "Python", "JavaScript">; std::cout 

Для определения двухмерного массива мы должны указать как минимум размер вложенных массивов, который будет достаточным, чтобы вместить каждую строку. В данном случае размер каждого вложенного массива - 20 символов. Однако зачем для первой строки - "C++", которая содержит 4 символа (включая концевой нулевой байт) выделять аж 20 байтов? Это - ограничение подобных массивов. Массивы указателей же позволяют обойти подобное ограничение:

#include int main() < const char *langs[] < "C++", "Python", "JavaScript">; // перебор массива for(unsigned i<>; i < std::size(langs); i++) < std::cout >

В данном случае элементами массива langs являются указатели: 3 указателя, каждый из которых занимает 4 или 8 байт в зависимости от архитекутуры (размер адреса). Каждый из этих указателей указывает на адрес в памяти, где расположены соответствующие строки: "C++", "Python", "JavaScript". Однако каждая из этих строк будет занимать именно то пространство, которое ей непосредственно необходимо. То есть строка "С++" будет занимать 4 байта. С одной стороны, мы здесь сталкиваемся с дополнительными издержками: дополнительно выделяется память для хранения адресов в указателях. С другой стороны, когда строки в массиве сильно различаются по длине , то мы можем получить общий выигрыш в количестве потребляемой памяти.

Методы доступа к элементам массивов

В языке СИ между указателями и массивами существует тесная связь. Например, когда объявляется массив в виде int array[25], то этим определяется не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array, значение которого равно адресу первого по счету (нулевого) элемента массива, т.е. сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array. С точки зрения синтаксиса языка указатель arrey является константой, значение которой можно использовать в выражениях, но изменить это значение нельзя.

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

int arrey[25]; int *ptr; ptr=array;

Здесь указатель ptr устанавливается на адрес первого элемента масcива, причем присваивание ptr=arrey можно записать в эквивалентной форме ptr=&arrey[0].

Для доступа к элементам массива существует два различных способа. Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например, array[16]=3 или array[i+2]=7. При таком способе доступа записываются два выражения, причем второе выражение заключается в квадратные скобки. Одно из этих выражений должно быть указателем, а второе - выражением целого типа. Последовательность записи этих выражений может быть любой, но в квадратных скобках записывается выражение следующее вторым. Поэтому записи array[16] и 16[array] будут эквивалентными и обозначают элемент массива с номером шестнадцать. Указатель используемый в индексном выражении не обязательно должен быть константой, указывающей на какой-либо массив, это может быть и переменная. В частности после выполнения присваивания ptr=array доступ к шестнадцатому элементу массива можно получить с помощью указателя ptr в форме ptr[16] или 16[ptr].

Второй способ доступа к элементам массива связан с использованием адресных выражений и операции разадресации в форме *(array+16)=3 или *(array+i+2)=7. При таком способе доступа адресное выражение равное адресу шестнадцатого элемента массива тоже может быть записано разными способами *(array+16) или *(16+array).

При реализации на компьютере первый способ приводится ко второму, т.е. индексное выражение преобразуется к адресному. Для приведенных примеров array[16] и 16[array] преобразуются в *(array+16).

Для доступа к начальному элементу массива (т.е. к элементу с нулевым индексом) можно использовать просто значение указателя array или ptr. Любое из присваиваний

*array = 2; array[0] = 2; *(array+0) = 2; *ptr = 2; ptr[0] = 2; *(ptr+0) = 2;

присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array=2 и *ptr=2, так как в них не требуется выполнять операции сложения.

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

Указатели на многомерные массивы в языке СИ - это массивы массивов, т.е. такие массивы, элементами которых являются массивы. При объявлении таких массивов в памяти компьютера создается несколько различных объектов. Например при выполнении объявления двумерного массива int arr2[4][3] в памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей. Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес массива из трех элементов типа int, и, следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов. Такое выделение памяти показано на схеме на рис.3.

Обращение к элементам массива через указатели

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

94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Обращение к элементам двумерного массива через указатели
Собсно, гуглил, вроде всё просто, но что-то плохо воспринимается. Имеется 2d массив. data1 = .

Обращение к элементам динамического массива через указатели
Само задание написано внизу в коментарии! Заранее спасибо! #include <conio.h> #include.

Доступ к элементам массива через указатели
Пишу программу для сортировки массива указателей // сортировка объектов через массив указателей.

Обращение к элементам двухмерного массива через указатель
Здравствуйте! Имеется класс class Analyzer < public: vector< vector<string> >.

Почетный модератор

Эксперт HTML/CSSЭксперт PHP

16843 / 6722 / 880
Регистрация: 12.06.2012
Сообщений: 19,967
int ** и int[][]/int*[] - разные вещи..

int (*pi)[m]=a;
scanf("%d",*(pi+i)+j);
printf("%d",*(*(pi+i)+j));

Эксперт PHP

4877 / 3880 / 1609
Регистрация: 24.04.2014
Сообщений: 11,371
Генрисон,

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#include #define N 2 #define M 3 int main(void) { int a[N][M] = { {0, 1, 2}, {3, 4, 5} }; // можно так int* p = &a[0][0]; for (int i = 0; i  N; ++i) { for (int j = 0; j  M; ++j) { printf("%d ", *(p + i*M + j)); } printf("\n"); } // или так int (*pa)[M] = a; for (int i = 0; i  N; ++i) { for (int j = 0; j  M; ++j) { printf("%d ", *( *(pa+i) + j)); } printf("\n"); } // еще, например, так for (int* i = p; i  &a[N-1][M-1]; ++i) { printf("%d ", *i); if ( (i-p) % M == M-1 ) { printf("\n"); } } // или вот так for (int (*i)[M] = pa; i  &a[N]; ++i) { for (int j = 0; j  M; ++j) { printf("%d ", *(*i+j)); } printf("\n"); } return 0; }

Регистрация: 30.10.2012
Сообщений: 347

Jewbacabra, подскажите,а как мне сделать обращение к элементу в случае,если я хочу,например,сделать так: если второй элемент в первом столбце больше 5,то присвоить ему значение 2 ? Я понимаю,что в форме обращения [] это будет выглядеть вот так:

if(a[1][0] > 5) a[1][0]=2;

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *