Указатели и многомерные массивы.
Пример объявления двумерного массива значений типа int: int a[m][n];
Этот массив состоит из m одномерных массивов (строк), в каждом из которых содержится nэлементов (столбцов). При работе с этим двумерным массивом можно использовать 1 или 2 индексных выражения. Например:
a[i][j]- содержит 2 индекса; используется для обращения к элементу i-строки, j-столбца массива; вычисляются индексные выражения, определяется адрес элемента массива и извлекается его значение;
a[i]- содержит 1 индекс; определяет адрес одномерного массива: адрес начала i-строки массива;
а- не содержит индекса и определяет адрес массива, его нулевого элемента.
Таким образом, обращение к двумерным массивам с помощью имени и только одного индекса определяет указатель на начало соответствующей строки массива (адрес ее нулевого элемента). Например:
а[0] = = &а[0][0] = = а + 0 * n * sizeof(int);
a[l] = = &а[1][0] == a + 1 * n * sizeof(int);
a[i] == &а[i][0] == a + i * n * sizeof(int);
Пример: int mas[4][2];
Двумерный массив можно представить как массив массивов. В нашем случае мы имеем четырех элементный массив, состоящий из двух элементов. Примечательно, что этот четырех элементный массив можно представить в виде одномерного mas[0],…,mas[3]. При этом имя массива по-прежнему является указателем на его первый элемент, т.е. mas[0]= =&mas[0] [0] . На что же будут указывать mas[i]? В этом случае mas [i] указывает на i-тую строку, т.е. на первый элемент i - й строки. Таким образом
mas [0] == &mas [0] [0];
mas [1] == &mas [1] [0];
mas[2] == &mas [2] [0];
mas[3] == &mas [3] [0];
Для доступа к отдельному элементу массива используется конструкция вида a[i][j], гдеiиjвыражения целочисленного типа.
Можно обратиться к элементу массива и другими способами: Выражение a[i][j] компилятор Си++ переводит в эквивалентное выражение *(*(а + i) + j). Разумеется, запись a[i][j] более традиционна в математике и более наглядна.
Обращения к элементу массива *(*(а + i) + j) или *(a[i] + j) приведены для лучшего понимания механизма индексации, поскольку здесь в явном виде записаны те же действия, которые генерируются компилятором при обычном обращении к массиву. Рассмотрим их подробнее. Допустим, требуется обратиться к элементу, расположенному на пересечении второй строки и третьего столбца — а[2][3]. Как и для одномерных массивов, имя массива а представляет собой константный указатель на начало массива. В данном случае это массив, состоящий из трех массивов. Сначала требуется обратиться ко второй строке массива, то есть одномерному массиву а[2]. Для этого надо прибавить к адресу начала массива смещение, равное номеру строки, и выполнить разадресацию: *(а + 2). Напомним, что при сложении указателя с константой учитывается длина адресуемого элемента, поэтому на самом деле прибавляется число 2, умноженное на длину элемента, то есть 2 * (5 * sizeof(int)), пocкольку элементом является строка, состоящая из 5 элементов типа int.
Далее требуется обратиться к третьему элементу полученного массива. Для получения его адреса опять применяется сложение указателя с константой 3 (на самом деле прибавляется 3 * sizeof(int)), а затем применяется операция разыменования для получения значения элемента: *(*(а + 2) + 3).
При этом для обращения к массиву а можно использовать имена:
&а = а = &а[0] [0] = *а - адрес а[0] [0]-элемента 0-строки 0-столбца массива.
**а = * (&a[0] [0]) = а [0] [0] - значение элемента 0-строки 0-столбца массива а
a[i]=(а + i) = *(а + i) = &a[i][0] - адрес начала i-строки: адрес элемента i-строки 0-столбца;
*a[i] = **(а + i) = *(&a[i][0]) = a[i][0] - значение 0-элемента i-строки
a[i][j] = *(*(а + i) + j) = *{a[i] + j) = a[i] [j] -значение элемента i-строки j-столбца массива а;
где (a + i) = *(a + i) = a[i] - адрес 0-элемента i-строки = &a[i] [0];
(*(a + i) + j) - адрес j-элемента i-строки = &a[i][j];
*(*(a+i) +j) - значениеj-элементаi-строки =a[i][j].
Значение адреса начала i-строки (адреса 0-элемента i-строки) на машинном уровне формируется в виде:
a[i] =a+i= (а + i * n * sizeof(int)),
где n- количество значений в одной строке.
Пример: Рассмотрим двумерный массив и действия с указателями.
int mas[4][2];
int *ptr;
ptr = mas;
ptr сейчас указывает на первый столбец первой строки, т.е.
ptr = = mas = = &mas [0] [0];
Увеличим указатель:
ptr+1 = = &mas [0] [1];
ptr+2 = = &mas [1] [0];
ptr+3 = = &mas [1] [1] и т.д.
Пример:
#include <stdio.h>
#include <conio.h>
void main()
{ int a[6][5], i, j;
printf("\nRazmer massiva à = %d bait\n", sizeof(a));
for ( i = 0; i < 5; i++)
{ for ( j = 0; j < 6; j++)
{ *(* (a+i)+j) = i + j;
printf(" a[%d][%d] = %d ", i, j, a[i][j]); }
printf("\n"); }
getch();}
Результат работы программы:
Razmer massiva р = 120 bait
a[0][0] = 0 a[0][1] = 1 a[0][2] = 2 a[0][3] = 3 a[0][4] = 4 a[0][5] = 5
a[1][0] = 1 a[1][1] = 2 a[1][2] = 3 a[1][3] = 4 a[1][4] = 5 a[1][5] = 6
a[2][0] = 2 a[2][1] = 3 a[2][2] = 4 a[2][3] = 5 a[2][4] = 6 a[2][5] = 7
a[3][0] = 3 a[3][1] = 4 a[3][2] = 5 a[3][3] = 6 a[3][4] = 7 a[3][5] = 8
a[4][0] = 4 a[4][1] = 5 a[4][2] = 6 a[4][3] = 7 a[4][4] = 8 a[4][5] = 9