
Двумерные массивы в c. Массив указателей. Типичные ошибки.
I. На кого рассчитан модуль.
Данный модуль рассчитан учащегося в старшей школе или студента. Должны быть освоены такие темы как работа с циклами, с указателями, необходимо знать, что такое одномерный массив.
II. Мотивация
Мы уже знаем, для чего нам могут понадобиться одномерные массивы. Например, мы можем сохранить в одномерном массиве коэффициенты квадратного уравнения и посчитать корни, используя дискриминант. А если нам нужно посчитать много таких уравнений? Неужели каждый раз перезаписывать новые коэффициенты на место старых? А если необходимо будет вернуться к уже посчитанным? Или нам нужно решить систему из нескольких уравнений, т.е. нужно одновременно работать со всеми? Если нам просто нужно хранить таблицу каких-то значений, например, измерений при опыте, как нам это сделать, используя полученные уже знания?
Для каждого из вышеназванных случаев можно просто объявить несколько одномерных массивов, но вот приятно ли будет работать с таким количеством? Ведь каждый нужно будет назвать и обработать отдельно. Ситуация похожа на ту, когда мы только вводили понятие одномерного массива. Тут и приходят на помощь массивы двумерные.
III. Изложение материала модуля
Обычным представлением таких массивов являются таблицы значений, содержащие информацию в строках и столбцах. Чтобы определить отдельный табличный элемент, нужно указать два индекса: первый (по соглашению) указывает номер строки, а второй (опять же по соглашению) указывает номер столбца.
Рисунок иллюстрирует двумерный массив a. Массив содержит три строки и четыре столбца, так что, еще говорят, - это массив три на четыре. Вообще, массивы с m строками и n столбцами называют массивами m на n.
Каждый элемент в массиве а определяется именем элемента в форме a[ i ][ j ]; a – это имя массива, а i и j – индексы, которые однозначно определяют каждый элемент в а. Заметим, что имена элементов первой строки имеют первый индекс 0, имена элементов в четвертом столбце имеют второй индекс 3.
Типичная ошибка программирования
Неправильная ссылка на элемент двумерного массива a[x] [y] как a[x,y]. На самом деле, a[x,y] воспринимается как a[y], потому что С оценивает выражение (содержащее операцию последования - запятую) x, y просто как y (последнее из разделенных запятыми выражений).
Многомерные массивы могут получать начальные значения в своих объявлениях точно так же, как массивы с естественным индексом. Например, двумерный массив b[2] [2] можно объявить и дать ему начальные значения таким образом:
int b[2] [2] = {{1,2}, {3,4}};
Значения группируются в строки, заключенные в фигурные скобки. Таким образом, элементы b[0] [0] и b[0] [1] получают начальные значения 1 и 2, а элементы b[1] [0] и b[1] [1] получают начальные значения 3 и 4. Если начальных значений в данной строчке не хватает для их присвоения всем элементам строки, то остающимся элементам присваиваются нулевые начальные значения. Таким образом, объявление
int b[2][2] = {{1,},{3,4}};
будет означать, что b[0][0] получает начальное значение 1, b[0][1] получает начальное значение 0.
Объем памяти в байтах, занимаемый двухмерным массивом, вычисляется по следующей формуле:
Количество байтов = размер_1-го_измерения*размер_2-го_измерения*sizeof(базовый тип)
Например, двумерный массив 4-байтовых целых чисел размерностью 10*5 занимает участок памяти объемом
10*5*4,
то есть 200 байтов.
Передача массива в функцию.
Рассмотрим небольшую программу вывода элементов двумерного массива:
#include <stdio.h>
void printArray(int a[][3])
{
for (int i = 0; i<=1;i++)
{
for (int j=0; j<=2; j++)
printf(“%i, ”,&a[i][j]);
printf(“\n”);
}
}
void main()
{
int array[2][3] = {{1,2,3},{4,5,6}};
printf(“Values in array on rows:”);
printgArray(array);
return 0;
}
Values in array on rows:
1 2 3
4 5 6
Программа вызывает функцию printArray для вывода элементов массива. Заметим, что описание функции указывает параметр – массив как int a[ ] [3]. Когда мы задаем как аргумент функции одномерный массив, скобки в списке параметров функции пусты. Размерность первого индекса многомерного массива также не требуется, но все последующие размерности индексов необходимы. Компилятор использует размерности этих индексов для определения соответствующих ячеек памяти для доступа к элементам многомерных массивов. В памяти все элементы массива хранятся последовательно, независимо от количества индексов (размерности массива). В двумерном массиве первая строка хранится в памяти перед второй строкой.
Наличие размерностей индексов в объявлении параметра дает возможность компилятору сообщить функции о том, как расположены элементы в массиве. В двумерном массиве каждая строка является одномерным массивом. Чтобы определить местоположение элемента в некоторой строке, функция должна точно знать, сколько элементов находится в каждой строке. Тогда функция может пропустить соответствующее количество ячеек памяти при обращении к массиву. Таким образом, при обращении к a[1] [2] функция знает, что для доступа ко второй строке (строка 1) нужно пропустить в памяти три элемента первой строки, а затем обратиться к третьему элементу этой строки (элементу 2).
Многие типовые операции с массивами используют конструкцию for. Так, следующий цикл определяет сумму всех элементов массива a:
total = 0;
for (row = 0; row < 3; row++ )
for (column = 0; column < 3; column ++ )
total += a [row] [column];
Внутренняя структура for суммирует элементы одной строки массива. Внешняя структура for начинает работу с установки row (т.е. индекса строки) в нуль, так что во внутренней структуре for могут быть просуммированы элементы второй строки. Далее внешняя структура for увеличивает row до значения 2, так что могут быть просуммированы элементы третьей строки. После завершения работы вложенной структуры for печатается результат.
Задача
Учащиеся одного из ВУЗов как-то поспорили, у кого наивысший средний бал за только что сданную сессию. Заодно захотели выяснить, кто получил наилучшую и наихудшую оценки. Для интереса представим, что мы живем в Америке, и оценки выставляются по 100-бальной шкале.
Указание:
Программа должна «знать» имена студентов, выводить их на экран. То же самое с таблицей успеваемости. Подразумевается, что номера столбцов эквивалентны номерам экзаменов.
Размышления о решении задачи
Для начала давайте определимся, что нам нужно для решения этой задачи. Первое, на что мы обращаем внимание – это то, что в условие явно сказано слово «таблица». Значит, скорее всего, для работы с ней мы будем использовать двумерный массив. Каждая строка в нем – это успеваемость отдельного студента. Как и сказано в условии, номер столбца будет определять номер сданного экзамена, номер строки – студента. Именно с этим массивом мы и будем работать, вычисляя средний бал, минимум и максимум. Видно, что необходимо будет создать три функции для получения нужных нам данных и функцию вывода таблицы оценок на экран. Все, что нужно для написания этой части программы, мы уже знаем.
Теперь разберемся с именами. Неплохо было бы их скомпоновать в таблицу, для того, чтобы по номеру студента можно было определить его имя и наоборот. Мы уже знаем. Что строка – это одномерный массив символов. Значит, можно создать двумерный массив, в котором номер строки будет определять номер студента, а номер столбца – соответствующую букву имени. Но тогда придется создать массив с наибольшим количеством ячеек по горизонтали из возможных. Т.е., если у нас есть четыре студента:
Ivanov
Velnikovskiy
Nilov
Petrov,
То необходимо будет создать таблицу, подобную изображенной на рисунке:
I |
v |
a |
n |
o |
v |
|
|
|
|
|
|
V |
e |
l |
n |
i |
k |
o |
v |
s |
k |
i |
y |
N |
i |
l |
o |
v |
|
|
|
|
|
|
|
P |
e |
t |
r |
o |
v |
|
|
|
|
|
|
Серым цветом на рисунке показаны пустые клетки нашего массива. На эти клетки будет отведено место в памяти, но вот пользоваться они не будут. Так зачем они нам нужны? Нет ли способа избежать такой ситуации? Оказывается, есть. Об этом будет рассказано ниже, а затем мы вернемся к решению нашей задачи.