Двумерные массивы: задачи поиска, замены и суммирования элементов двумерного массива
Определение размера памяти двумерных массивов
В языке С++ определены только одномерные массивы, но поскольку элементом массива может быть массив, возможно определить и двумерные массивы. Они определяются списком константных-выражений следующих за идентификатором массива, причем каждое константное-выражение заключается в свои квадратные скобки. Каждое константное-выражение в квадратных скобках определяет число элементов по данному измерению массива, так что объявление двумерного массива содержит два константных-выражения, трехмерного – три и т.д.
спецификатор-типа имя_массива [конст_выражение1] [конст_выражение2];
Например,
int a[2][3]; /* представлено в виде матрицы a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2]*/
Объем занимаемой памяти в байтах для двухмерного массива вычисляется по формуле:
Байты = sizeof (тип) * конст_выражение1* конст_выражение2
Если мы имеем дело с двумерным массивом B размерности MxN, расположенным в памяти по строкам, то адрес элемента B[i][j] вычисляется по формуле:
адрес(B[i][j]) = адрес(B[0][0]) + (i*N+j)*k
Так как массивы занимают непрерывный участок памяти, то двумерный массив размерности MxN можно рассматривать как одномерный массив из M указателей, которые являются константами. Константы-указатели содержат значения адресов M одномерных безымянных массивов. Поэтому обращение к элементу B[i][j] посредством B[i*N + j] невозможно, так как указателя с номером i*N + j может не существовать.
Пример 1. Определение размера памяти двумерного массива
#include "stdafx.h" #include <iostream> using namespace std; #define v 4 #define p 3 int _tmain(int argc, _TCHAR* argv[]){ const int q=4, r=1; int i_mas[10][10]; int k=sizeof(i_mas); cout << "i_mas[10][10] занимает " << k << " байт\n"; float f_mas[3][5]={{2.0},{4.5,8.3},{7.0,1.0,5.5,7.8}}; int t=sizeof(f_mas); cout << "f_mas[3][5]={{2.0},{4.5,8.3},{7.0,1.0,5.5,7.8}} занимает " << t << " байт\n"; double d_mas[2*q-r][2*v/p]; int w=sizeof(d_mas); cout << "d_mas[2*q-r][2*v/p] занимает " << w << " байт\n"; int r_mas[][3]={{2,5,7},{-4,8,-3},{0,-1,1}}; int g=sizeof(r_mas); cout << "r_mas[][3]={{2,5,7},{-4,8,-3},{0,-1,1}} занимает " << g << " байт\n"; system("pause"); return 0; }
Результат выполнения программы:
i_mas[10][10] занимает 400 байт – 4 байта (тип int ) * 10*10 (количество элементов массива)
f_mas[3][5]={{2.0},{4.5,8.3},{7.0,1.0,5.5,7.8}} занимает 60 байт – 4 байта (тип float ) * 3*5 (объявленное количество элементов массива)
d_mas[2*q-r] [2*v/p] занимает 112 байт – 8 байт (тип double ) * 7*2 (вычисленное через формулу количество элементов массива)
r_mas[][3]={{2,5,7},{-4,8,-3},{0,-1,1}} занимает 36 байт – 4 байта (тип int ) * 3*3 (заданное количество элементов массива)
Указатели и двумерные массивы
При размещении элементов двумерных массивов они располагаются в памяти подряд по строкам, т.е. быстрее всего изменяется последний индекс, а медленнее – первый. Такой порядок дает возможность обращаться к любому элементу двумерного массива, используя адрес его начального элемента и только одно индексное выражение.
int arr[m][n]: Адрес (arr[i][j])= Адрес(arr[0][0]) + (i*n+j)*k,
где k – количество байтов, выделяемое для элемента массива (в зависимости от типа).
Указатели на двумерные массивы в языке С++ – это массивы массивов, т.е. такие массивы, элементами которых являются массивы. При объявлении таких массивов в памяти компьютера создается несколько различных объектов. Например, при выполнении объявления двумерного массива:
int arr [4][3];
- В памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей.
- Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес одномерного массива из трех элементов типа int.
- Следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов.
Схематично распределение памяти для данного двумерного массива выглядит так:
Таким образом, объявление arr[4][3] порождает в программе три разных объекта:
- указатель с идентификатором arr,
- безымянный массив из четырех указателей: arr[0], arr[1], arr[2], arr[3]
- безымянный массив из двенадцати чисел типа int.
- Для доступа к безымянным массивам используются адресные выражения с указателем arr. Доступ к элементам одномерного массива указателей осуществляется с указанием одного индексного выражения в форме arr[2] или *(arr+2).
- Для доступа к элементам двумерного массива чисел типа int arr[i][j] должны быть использованы следующие выражения:
Например, пусть i=1, j=2, тогда обращение к элементу arr[1][2]:
- arr[i][j] arr[1][2]=10
- *(*(arr+i)+j) *(*(arr+1)+2)=10
- (*(arr+i))[j] (*(arr+1))[2]=10
Причем внешне похожее обращение arr[5] выполнить невозможно, так как указателя с индексом 5 не существует.
Пример 2. Использование индексных и адресных выражения при обработке двумерных массивов.
#include "stdafx.h" #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]){ int i, j; int t[2][3]; // при вводе обращение с помощью индексов // можно использовать адресные выражения for(i=0;i<2;i++) for(j=0;j<3;j++) t[i][j]=i+j; for(i=0;i<2;i++) for(j=0;j<3;j++) //при печати рассматриваем имя массива как указатель на начало printf(" %d ", *(*(t + i) +j) ); //или printf(" %d ", (*(t + i))[j]); system("pause"); return 0; }
Пример 3. Демонстрация связи между матрицей и указателем на нее.
#include "stdafx.h" #include <iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]){ int i,j; int t[2][3],*ptr; ptr=&t[0][0]; for(i=0;i<2;i++) for(j=0;j<3;j++) t[i][j]=i+j; for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", *(ptr+i*3+j)); printf("\n\n"); //С матрицей так делать нельзя /* for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", *(t + i*3 +j) ); */ //Корректная работа с матрицей for(i=0;i<6;i++) printf(" %d ", ptr[i] ); printf("\n\n"); for(i=0;i<2;i++) for(j=0;j<3;j++) printf(" %d ", ptr[i*3 +j] ); system("pause"); return 0; }