Многомерные статические массивы

Теги: Си многомерные массивы, инициализация многомерных массивов, массивы в памяти.



Многомерные статические массивы

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

<тип> <имя>[размерность1][размерность2]...;

Например, двумерный массив

int a[2][3];

Трёхмерный массив

int a[3][4][5];

Доступ до элементов массива осуществляется также, как и в одномерном массиве

#include <conio.h>
#include <stdio.h>

#define SIZE 5

void main() {
        int M[3][3];
        unsigned i, j;

        for (i = 0; i < 3; i++) {
                for (j = 0; j < 3; j++) {
                        M[i][j] = i * j;
                }
        }

        do {
                printf("enter indexes:\n");
                scanf("%d", &i);
                scanf("%d", &j);
                if (i < 3 && j < 3) {
                        printf("M[%d][%d] == %d\n", i, j, M[i][j]);
                } else {
                        break;
                }
        } while (1);

        for (i = 0; i < 3; i++) {
                for (j = 0; j < 3; j++) {
                        printf("\t%d", M[i][j]);
                }
                printf("\n");
        }
        getch();
}

Особенностью является то, что по своему строению многомерный массив является обыкновенным, "одномерным", массивом. Все элементы расположены друг за другом. Доступ до элемента a[i][j] – по существу сдвиг на i*число столбцов + j. В двумерном массиве, таким образом, элементы расположены "по рядам", в трёхмерном - "по слоям", внутри которых элементы расположены "по рядам" и т.д.
В связи с этим, при начальной инициализации опускать размерность можно только в первых квадратных скобках:

int a[][3] = {0};
int b[][5][25] = {0};

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

int a[2][3] = {1, 2, 3, 4, 5, 6};

Можно опустить первую размерность

int a[][2][3] = {1, 2, 3, 34, 5, 6, 7, 8, 9, 10, 11, 12};

Можно с помощью фигурных скобок сделать данные более удобными для чтения

int a[2][3] = {{1, 2, 3}, {4, 5, 6}}

или

int a[][3][4] = 
{{{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}},
 {{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}},
 {{1, 2, 3, 4}, {2, 4, 6, 8}, {3, 6, 9, 12}}};

Также, как и в одномерных массивах, если заявлено данных больше, чем указано при инициализации, то оставшиеся заполняются нулями. Например, единичная матрица 3 на 3

int zero3[3][3] = {{1}, {0, 1}, {0, 0, 1}};

Из того, что многомерный массив является одномерным по структуре, вытекают некоторые интересные свойства. Например, доступ до элемента может быть осуществлён через его порядковый номер
a[i][j] === a[0][i*число столбцов + j] и т.д.

Примеры

1. Отсортируем двумерный массив методом пузырька. Для сортировки обычно используется два подхода - превращение двумерного массива в одномерный, сортировка, обратно превращение одномерного в двумерный, либо запутанное обращение к элементам через индекс. Можно сделать всё проще: работать с многомерным массивом как с одномерным

#include <conio.h>
#include <stdio.h>

#define ROWS 4
#define COLS 3

void main() {
        int a[ROWS][COLS] = 
                {{1, 4, 5}, 
                 {2, 6, 8}, 
                 {1, 0, 9}, 
                 {4, 2, 8}};
        int i, j, tmp, flag;

        do {
                flag = 0;
                for (i = 1; i < ROWS*COLS; i++) {
                        if (a[0][i] < a[0][i-1]) {
                                tmp = a[0][i];
                                a[0][i] = a[0][i-1];
                                a[0][i-1] = tmp;
                                flag = 1;
                        }
                }
        } while(flag);

        for (i = 0; i < ROWS; i++) {
                for (j = 0; j < COLS; j++) {
                        printf("%3d", a[i][j]);
                }
                printf("\n");
        }

        _getch();
}

Замечание: по стандарту явно такое поведение не определено, но косвенно должно поддерживаться.

2. Даны координаты x и y точки, полученные в ходе фотосъёмки. Известно, сколько кадров в секунду делала камера. Вычислить скорость в каждый момент времени и среднюю скорость за всё время.

#include <conio.h>
#include <stdio.h>
#include <math.h>

#define SIZE 10

void main() {
        float a[2][SIZE] = 
                {{1.03, 1.52, 2.11, 2.53, 3.08, 3.48,  3.98,  4.51,  5.02,  5.17}, 
             {1.03, 2.45, 4.13, 5.64, 7.22, 8.73, 10.32, 11.75, 13.28, 14.87}};
        float velocity[SIZE-1];
        float speed = 0.0;
        float vx, vy;
        float dt = 0.1;
        int i;

        for (i = 0; i < SIZE-1; i++) {
                vx = ( a[0][i+1] - a[0][i] ) / dt;
                vy = ( a[1][i+1] - a[1][i] ) / dt;
                velocity[i] = sqrt(vx*vx + vy*vy);
                speed += velocity[i];
        }

        speed /= (float)(SIZE - 1);
        for (i = 1; i < SIZE; i++) {
                printf("v[%d] = %.3f m/s\n", i, velocity[i-1]);
        }
        printf("mean velocity = %.3f", speed);
        _getch();
}

3. Массив используется как карта, где число 2 означает начало, а 3 - конец пути. Программа сначала находит координаты этих точек, после этого вычисляет расстояние Манхеттена (сколько нужно пройти по x и y от начала до конца) и расстояние по Евклиду (как гипотенузу прямоугольного треугольника).

#include <conio.h>
#include <stdio.h>
#include <math.h>

#define SIZE 5
#define START  2
#define FINISH 3

void main() {
        char field[SIZE][SIZE] = {
                {0, 0, 0, 0, 0},
                {2, 0, 0, 0, 0},
                {0, 0, 0, 0, 0},
                {0, 0, 0, 0, 0},
                {0, 0, 0, 0, 3}
        };
        unsigned i, j;
        unsigned x, y;
        char xFound = 0;
        int X, Y;
        char XFound = 0;
        unsigned manhattanDist;
        float euclidDist;

        for (i = 0; i < SIZE; i++) {
                for (j = 0; j < SIZE; j++) {
                        if (field[i][j] == START) {
                                x = i;
                                y = j;
                                xFound = 1;
                                if (XFound) {
                                        break;
                                }
                        }
                        if (field[i][j] == FINISH) {
                                X = i;
                                Y = j;
                                XFound = 1;
                                if (xFound) {
                                        break;
                                }
                        }
                }
                if (xFound && XFound) {
                        break;
                }
        }
        if (!(xFound && XFound)) {
                printf("Error: corrupted data\n");
                getch();
                exit(1);
        }

        printf("(x,y) = %d, %d\n(X,Y)= %d, %d\n", x, y, X, Y);

        manhattanDist = abs((int)(x-X)) + abs((int)(y-Y));
        //тоже самое, что и sqrt((x-X)*(x-X)+(y-Y)*(y-Y))
        x -= X;
        y -= Y;
        euclidDist = sqrt((float)(x*x + y*y));
        printf("Manhattan dist. = %d\nEuclid dist. = %.3f", manhattanDist, euclidDist);
        getch();
}

4. Пользователь вводит 10 слов. Вывести слово с максимальной длиной. Программа внешне совершенно простая, единственная проблема - считывание и вывод слова. Так как слова храняться в двумерном массиве, то указатель на words[i][0] - это начало нового слова. Также не забываем об ограничении на длину при вводе.

#include <conio.h>
#include <stdio.h>

#define SIZE 10
#define MAX_LENGTH 128

void main() {
        //Массив хранит 10 слов максимум по 128 символов
        char words[SIZE][MAX_LENGTH];
        unsigned i, j, maxLength;
        //Так как длина слова ограничена 127 символами, то типа char хватит
        unsigned char counter[SIZE];

        for (i = 0; i < SIZE; i++) {
                //Считываем слова. words[i][0] - это символ, нам нужен
                //адрес, начиная с которого можно писать в массив
                fgets(&words[i][0], MAX_LENGTH - 1, stdin);
                j = 0;
                //Считаем длину слова
                while (words[i][j]) {
                        j++;
                }
                counter[i] = j;
        }

        //Ищем слово с максимальной длиной
        maxLength = counter[0];
        j = 0;
        for (i = 1; i < SIZE; i++) {
                if (counter[i] > maxLength) {
                        maxLength = counter[i];
                        j = i;
                }
        }

        //Выводим слово на печать. При выводе строки
        //необходимо передавать указатель
        printf("%s", &words[j][0]);
        _getch();
}
Q&A

Всё ещё не понятно? – пиши вопросы на ящик email
Массивы