Спонсор: Microsoft
Опубликован: 02.02.2011 | Доступ: свободный | Студентов: 2282 / 318 | Оценка: 4.43 / 3.57 | Длительность: 33:06:00
Специальности: Программист
Лекция 14:

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

< Лекция 13 || Лекция 14: 1234 || Лекция 15 >

Определение размера памяти двумерных массивов

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

спецификатор-типа имя_массива [конст_выражение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];
  1. В памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей.
  2. Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес одномерного массива из трех элементов типа int.
  3. Следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов.

Схематично распределение памяти для данного двумерного массива выглядит так:

arr
\\
\downarrow 
\\
arr[0]\ \to  arr[0][0]\ arr[0][1]\ arr[0][2]
\\
arr[1]\ \to  arr[1][0]\ arr[1][1]\ arr[1][2]
\\
arr[2]\ \to  arr[2][0]\ arr[2][1]\ arr[2][2]
\\
arr[3]\ \to  arr[3][0]\ arr[3][1]\ arr[3][2]

Таким образом, объявление arr[4][3] порождает в программе три разных объекта:

  • указатель с идентификатором arr,
  • безымянный массив из четырех указателей: arr[0], arr[1], arr[2], arr[3]
  • безымянный массив из двенадцати чисел типа int.
  1. Для доступа к безымянным массивам используются адресные выражения с указателем arr. Доступ к элементам одномерного массива указателей осуществляется с указанием одного индексного выражения в форме arr[2] или *(arr+2).
  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
    Например, с помощью указателя ptr:
    • ptr[1*3 + 2] 
       /*здесь 1 и 2 - это индексы используемого элемента, 
         а 3 это число элементов в строке*/
    • ptr[5]

Причем внешне похожее обращение arr[5] выполнить невозможно, так как указателя с индексом 5 не существует.

  • *(*(ptr + 1) + 2)
  • *(ptr + 1*3 + 2)

Пример 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;
}
< Лекция 13 || Лекция 14: 1234 || Лекция 15 >
Денис Курбатов
Денис Курбатов
Владислав Нагорный
Владислав Нагорный

Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки?

Спасибо!

Дмитрий Карпов
Дмитрий Карпов
Россия, Нижний Новгород
Сергей Злобин
Сергей Злобин
Россия, Подольск