Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на С.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать
☆

Зачем нужны двумерные массивы?

Двумерные массивы используются для хранения данных одинакового типа и одинаковой структуры. Приведу пример. В одном из практических заданий к прошлым урокам, необходимо было составить программу, которая хранила оценки ученика по разным предметам. Для их хранения в памяти компьютера мы использовали несколько одномерных массивов. Это было не очень удобно. Для этих же целей, мы могли использовать двумерный массив. Например, нам нужно хранить оценки ученика по русскому, математике, физике и информатике, за целый месяц. Для этого мы создадим массив с 4 строками и 30 столбиками (или наоборот, 30 строк и 4 столбика, кому как больше нравится), и в каждую отдельную строчку будем записывать оценки по какому либо предмету. Это гораздо удобнее и практичнее.

Например, рассмотрим такую задачку. Пусть нам нужно было бы найти среднее арифметическое всех оценок по всем предметам. Если бы у нас было четыре одномерных массива, то нам пришлось бы писать четыре цикла, в каждом из них считать среднее арифметическое оценок по одному предмету, потом находить среднее арифметическое этих четырех чисел. Если оценки у нас хранятся в двумерном массиве, нам достаточно будет просто пройтись по всем элементам массива, используя два вложенных цикла for, и найти среднее арифметическое.

Но это еще не всё. Представьте, что вы хотите добавить один предмет и снова посчитать среднюю арифметическую оценку для всех предметов. Сколько мороки будет, если вы храните оценки в одномерных массивах. Вам придется кучу изменений вносить, дописывать еще один цикл (а вдруг добавилось 7 предметов?). Потом еще среднее искать между этими цифрами.

Другое дело если вы храните оценки в двумерном массиве. Всего лишь изменить условие в одном из циклов. Поменять одно число, представляете как удобно?

Напишите в комментариях пожалуйста, понятен ли вам этот пример, или лучше его подробно расписать?

Если этот материал кажется вам полезным, расскажите о нем друзьям используя кнопки основных социальных сетей, расположенные ниже.

Практическое задание.

Напишите программу, работающую следующим образом. Создайте массив 10 на 10. Заполните его нулями. Считайте два произвольных целых числа с клавиатуры, меньших либо равных 10. Первое число количество строк, второе - количество столбцов. Напишите функцию, которая заполняет массив по спирали и выводит его на экран. Т.е. если бы мы ввели 6 и 7, то получили бы следующий массив.

При этом табличка приблизительно должна быть выровнена по центру окна вывода.

Занятие №20. Некоторые особенности цикла for. Оператор последовательного вычисления.

Выдалась свободная минутка, решил написать небольшой пост, по мотивам уже заданных мне ранее вопросов. Быть может кому-то эти советы, тоже будут полезны. Итак, сегодня займемся циклами.

Рассмотрим цикл for().

Как вы уже знаете, синтаксис этой конструкции имеет следующий вид:

for(инициализация счетчика; условие; изменение счетчика)

оператор

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

Если вы еще не забыли, а это не мудрено с моей-то частотой выпуска занятий, то последнем уроке мы изучали двойные массивы. Одной из типичных задач является вывод массива размерности [N][M] на экран в виде таблицы (или матрицы) в N-строк и M столбцов. Для этой задачи очень крайне удобно использовать два вложенных цикла for. Например:

Листинг 1.

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define M 8

int main(void){

int arr[N][M];

for (int i=0;i<N;i++)

for (int j=0; j<M; j++)

arr[i][j]=rand();

for (int i=0;i<N;i++)

for (int j=0; j<M; j++)

printf("%d\t",arr[i][j]);

return (0);

}

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

Листинг 2.

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define M 8

int main(void){

int arr[N][M];

for (int i=0;i<N;i++)

for (int j=0; j<M; j++)

arr[i][j]=rand();

for (int i=0;i<N;i++){

for (int j=0; j<M; j++)

printf("%d\t",arr[i][j]);

printf("\n");

}

return (0);

}

Вот так уже лучше. Так, наша программа будет выводить все так, как нужно. Вот, посмотрите на следующую картинку.

Рис.1. Результат работы программы.

А теперь поговорим о красоте. Этот вот дополнительный перенос строки , он как шило в заднице. Из-за него одного пришлось добавить составной оператор. Некрасиво получилось. Есть ли способ этого избежать. Да, есть!

Я вам раньше не говорил, а теперь скажу. В заголовке цикла for на месте инициализации счетчика и на месте изменения счетчика, могут стоять не одна, а сразу несколько инструкций. Чтобы их туда записать нужно использовать еще один оператор, оператор последовательного выполнения – ,. Да-да, просто запятая.

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

Листинг 3.

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define M 8

int main(void){

int arr[N][M];

for (int i=0;i<N;i++)

for (int j=0; j<M; j++)

arr[i][j]=rand();

for (int i=0;i<N;i++,printf("\n"))

for (int j=0; j<M; j++)

printf("%d\t",arr[i][j]);

return (0);

}

На экране мы изменений не увидим, а код стал меньше и приятнее. ) Вот видите, как может помочь оператор последовательного выполнения. Кстати, обратите внимание, если мы пишем инструкции внутри заголовка цикла, мы не ставим там дополнительных ;. Они там не нужны.

Рассмотрим один показательный пример. Используем для вывода двойного массива на экран один цикл for. Это будет выглядеть например так:

Листинг 4:

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define M 8

int main(void){

int arr[N][M];

for (int i=0;i<N;i++)

for (int j=0; j<M; j++)

arr[i][j]=rand();

for (int i=0, j=0 ;i<N; j++){

printf("%d\t",arr[i][j]);

if (M-j==1){

printf("\n");

i++;

j=-1;

}

}

return (0);

}

А на мониторе снова никаких изменений. ))

Рис.2. Результат работы программы.

Но такие штуки лучше не проворачивать, так как это может сильно запутать код. Я не буду подробно комментировать этот цикл, попробуйте разобраться самостоятельно, как он работает. Возьмите листочек и пошагово выполните его, будто бы вы компьютер.

UPD(от 2.02.14): Я говорил же вам, что этот код может запутать любого. Я и сам попался в эту ловушку. Записанный ранее код, был ошибочен. Стараниями внимательного читателя он исправлен, и теперь работает так, как ему и подобает. =))

Я не помню, рассказывал я о именованных константах, которые использую в своих примерах в этом уроке. На всякий случай расскажу еще раз.

Для объявления именованных констант служит директива #define. Её синтаксис таков

#define имя значение

Важно! Не нужно ставить в конце точку с запятой. А так же между именем и значением знак присвоения =.

Как работает эта инструкция, рассмотрим на нашем примере. Перед тем как компилятор начнет обрабатывать программу, он пройдется по всему коду и заменит все места, где встречается N или M и вместо них подставит в код их значения: 10 и 8 соответственно. Причем компилятору вообще по барабану, что на что мы заменяем. С учетом этой особенности, некоторые используют при написании код «Боярский язык». Ну т.е. пишут код на обычном русском языке. Например вот так.

Листинг 5.

#include <stdio.h>

#include <stdlib.h>

#define N 10

#define M 8

#define целое int

#define присвоить =

целое main(void){

целое arr[N][M];

for (целое i присвоить 0;i<N;i++)

for (целое j присвоить 0; j<M; j++)

arr[i][j] присвоить rand();

for (целое i присвоить 0, j присвоить 0 ;i<M,j<N;i++){

printf("%d\t",arr[i][j]);

if (M-i==1){

printf("\n");

j++;