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

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



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

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

<тип> <имя>[размерность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
Массивы