Обработка массивов записей. пример курсовой. Краткий обзор языка Основная часть 1 Массив. 2 Одномерные массивы
Скачать 49.2 Kb.
|
Рассмотрим двумерный массив Паскаля размерностью 3*3, то есть в ней будет три строки, а в каждой строке по три элемента: Каждый элемент имеет свой номер, как у одномерных массивов, но сейчас номер уже состоит из двух чисел – номера строки, в которой находится элемент, и номера столбца. Таким образом, номер элемента определяется пересечением строки и столбца. Например, a 21 – это элемент, стоящий во второй строке и в первом столбце. Описание двумерного массива Паскаля. Существует несколько способов объявления двумерного массива Паскаля. Мы уже умеем описывать одномерные массивы, элементы которых могут иметь любой тип, а, следовательно, и сами элементы могут быть массивами. Рассмотрим следующее описание типов и переменных: Пример описания двумерного массива Паскаля Type Vector = array [1..5] of <тип_элементов>; Matrix= array [1..10] of vector; Var m: matrix; Мы объявили двумерный массив Паскаля m, состоящий из 10 строк, в каждой из которых 5 столбцов. При этом к каждой i -й строке можно обращаться m [ i ], а каждому j -му элементу внутри i -й строки – m [ i , j ]. Определение типов для двумерных массивов Паскаля можно задавать и в одной строке: Type Matrix= array [1..5] of array [1..10] of < тип элементов >; или еще проще: type matrix = array [1..5, 1..10] of <тип элементов>; Обращение к элементам двумерного массива имеет вид: M [ i , j ]. Это означает, что мы хотим получить элемент, расположенный в i -й строке и j -м столбце. Тут главное не перепутать строки со столбцами, а то мы можем снова получить обращение к несуществующему элементу. Например, обращение к элементу M [10, 5] имеет правильную форму записи, но может вызвать ошибку в работе программы. Основные действия с двумерными массивами Паскаля Все, что было сказано об основных действиях с одномерными массивами, справедливо и для матриц. Единственное действие, которое можно осуществить над однотипными матрицами целиком – это присваивание. Т.е.,если в программе у нас описаны две матрицы одного типа, например, type matrix= array [1..5, 1..10] of integer; var a , b : matrix ; то в ходе выполнения программы можно присвоить матрице a значение матрицы b ( a := b ). Все остальные действия выполняются поэлементно, при этом над элементами можно выполнять все допустимые операции, которые определены для типа данных элементов массива. Это означает, что если массив состоит из целых чисел, то над его элементами можно выполнять операции, определенные для целых чисел, если же массив состоит из символов, то к ним применимы операции, определенные для работы с символами. Ввод двумерного массива Паскаля Для последовательного ввода элементов одномерного массива мы использовали цикл for, в котором изменяли значение индекса с 1-го до последнего. Но положение элемента в двумерном массиве Паскаля определяется двумя индексами: номером строки и номером столбца. Это значит, что нам нужно будет последовательно изменять номер строки с 1-й до последней и в каждой строке перебирать элементы столбцов с 1-го до последнего. Значит, нам потребуется два цикла for , причем один из них будет вложен в другой. Рассмотрим пример ввода двумерного массива Паскаля с клавиатуры: type matrix= array [1..5, 1..10] of integer; var a, : matrix; i, j: integer; { индексы массива } begin for i :=1 to 5 do {цикл для перебора всех строк} for j :=1 to 10 do {перебор всех элементов строки по столбцам} readln ( a [ i , j ]); {ввод с клавиатуры элемента, стоящего в i -й строке и j -м столбце} Двумерный массив Паскаля можно заполнить случайным образом, т.е. использовать функцию random (N), а также присвоить каждому элементу матрицы значение некоторого выражения. Способ заполнения двумерного массива Паскаля выбирается в зависимости от поставленной задачи, но в любом случае должен быть определен каждый элемент в каждой строке и каждом столбце. Вывод двумерного массива Паскаля на экран Вывод элементов двумерного массива Паскаля также осуществляется последовательно, необходимо напечатать элементы каждой строки и каждого столбца. При этом хотелось бы, чтобы элементы, стоящие в одной строке, печатались рядом, т.е. в строку, а элементы столбца располагались один под другим. Для этого необходимо выполнить следующую последовательность действий (рассмотрим фрагмент программы для массива, описанного в предыдущем примере): Пример программы вывода двумерного массива Паскаля for i :=1 to 5 do {цикл для перебора всех строк} begin for j :=1 to 10 do {перебор всех элементов строки по столбцам} write ( a [ i , j ]:4); {печать элементов, стоящих в i -й строке матрицы в одной экранной строке, при этом для вывода каждого элемента отводится 4 позиции} writeln ; {прежде, чем сменить номер строки в матрице, нужно перевести курсор на начало новой экранной строки} end ; Замечание (это важно!): очень часто в программах студентов встречается ошибка, когда ввод с клавиатуры или вывод на экран массива пытаются осуществить следующим образом: readln (a), writeln (a), где а – это переменная типа массив. При этом их удивляет сообщение компилятора, что переменную этого типа невозможно считать или напечатать. Может быть, вы поймете, почему этого сделать нельзя, если представите N кружек, стоящих в ряд, а у вас в руках, например, чайник с водой. Можете вы по команде «налей воду» наполнить сразу все кружки? Как бы вы ни старались, но в каждую кружку придется наливать отдельно. Заполнение и вывод на экран элементов массива также должно осуществляться последовательно и поэлементно, т.к. в памяти ЭВМ элементы массива располагаются в последовательных ячейках. Представление двумерного массива Паскаля в памяти Элементы абстрактного массива в памяти машины физически располагаются последовательно, согласно описанию. При этом каждый элемент занимает в памяти количество байт, соответствующее его размеру. Например, если массив состоит из элементов типа integer , то каждый элемент будет занимать по два байта. А весь массив займет S^2 байта, где S – количество элементов в массиве. А сколько места займет массив, состоящий из массивов, т.е. матрица? Очевидно: S i^S j , где S i - количество строк, а S j – количество элементов в каждой строке. Например, для массива типа Matrix = array [1..3, 1..2] of integer ; потребуется 12 байт памяти. Как будут располагаться в памяти элементы этого массива? Рассмотрим схему размещения массива M типа matrix в памяти. Под каждый элемент M [i,j] типа integer выделяется две ячейки памяти. Размещение в памяти осуществляется «снизу вверх». Элементы размещаются в порядке изменения индекса, что соответствует схеме вложенных циклов: сначала размещается первая строка, затем вторая, третья... Внутри строки по порядку идут элементы: первый, второй и т.д. Как мы знаем, доступ к любой переменной возможен, только если известен адрес ячейки памяти, в которой хранится переменная. Конкретная память выделяется для переменной при загрузке программы, то есть устанавливается взаимное соответствие между переменной и адресом ячейки. Но если мы объявили переменную как массив, то программа «знает» адрес начала массива, то есть первого его элемента. Как же происходит доступ ко всем другим элементам массива? При реальном доступе к ячейке памяти, в которой хранится элемент двумерного массива, система вычисляет ее адрес по формуле: Addr + Size Elem * Cols *( I -1)+ Size Elem *( J -1), где Addr – фактический начальный адрес, по которому массив располагается в памяти; I , J – индексы элемента в двумерном массиве; SizeElem – размер элемента массива (например, два байта для элементов типа integer ); Cols – количество элементов в строке. Выражение SizeElem * Cols *( I -1)+ SizeElem *( J -1) называют смещением относительно начала массива. Примеры решения задач с двумерными массивами Паскаля Задача: Найти произведение ненулевых элементов матрицы. Решение: Для решения данной задачи нам потребуются переменные: матрица, состоящая, например, из целочисленных элементов; P – произведение элементов, отличных от 0; I , J – индексы массива; N , M – количество строк и столбцов в матрице. Входными данными являются N , M – их значения введем с клавиатуры; матрица – ввод матрицы оформим в виде процедуры, заполнение матрицы осуществим случайным образом, т.е. с помощью функции random (). Выходными данными будет являться значение переменной P (произведение). Чтобы проверить правильность выполнения программы, необходимо вывести матрицу на экран, для этого оформим процедуру вывода матрицы. Ход решения задачи: обсудим сначала выполнение основной программы, реализацию процедур обговорим чуть позже: введем значения N и M ; Введем двумерный массив Паскаля, для этого обращаемся к процедуре vvod ( a ), где а – матрица; Напечатаем полученную матрицу, для этого обращаемся к процедуре print ( a ); Присвоим начальное значение переменной P =1; Будем последовательно перебирать все строки I от 1-й до N -й, в каждой строке будем перебирать все столбцы J от 1-го до M -го, для каждого элемента матрицы будем проверять условие: если a ij ? 0, то произведение P будем домножать на элемент a ij ( P = P * a ij ); Выведем на экран значение произведения ненулевых элементов матрицы – P ; А теперь поговорим о процедурах. Замечание (это важно!) Параметром процедуры может быть любая переменная предопределенного типа, это означает, что для передачи в процедуру массива в качестве параметра, тип его должен быть описан заранее. Например : Type Matrix=array [1..10, 1..10] of integer; .............................. procedure primer (a: matrix); .............................. Вернемся теперь к нашим процедурам. Процедура ввода матрицы называется vvod , параметром процедуры является матрица, причем она должна быть, как результат, передана в основную программу, следовательно, параметр должен передаваться по ссылке. Тогда заголовок нашей процедуры будет выглядеть так: Procedure vvod ( var m : matrix ); Для реализации вложенных циклов в процедуре нам потребуются локальные переменные-счетчики, например, k и h . Алгоритм заполнения матрицы уже обсуждался, поэтому не будем его повторять. Процедура вывода матрицы на экран называется print , параметром процедуры является матрица, но в этом случае она является входным параметром, следовательно, передается по значению. Заголовок этой процедуры будет выглядеть следующим образом: Procedure print ( m : matrix ); И вновь для реализации вложенных циклов внутри процедуры нам потребуются счетчики, пусть они называются так же – k и h . Алгоритм вывода матрицы на экран был описан выше, воспользуемся этим описанием. Пример программы двумерного массива Паскаля Program proizvedenie; Type Matrix=array [1..10, 1..10] of integer; Var A: matrix; N, m, i, j: byte; P: integer; Procedure vvod (var m: matrix); Var k , h : byte ; Begin For i :=1 to n do {переменная n для процедуры является глобальной, а значит «известной»} For j :=1 to m do {переменная m для процедуры является глобальной, а значит «известной»} M[i,j]:= random(10); End; Procedure print (m: matrix); Var k, h: byte; Begin For i:=1 to n do begin For j:=1 to m do Write (M[i, j]: 4); Writeln; end ; End ; Begin {начало основной программы} Writeln (‘Введите размерность матрицы:’); Readln(N, M); Vvod(a); 14 Print(a); P:=1; For i:=1 to N do For j:=1 to M do If a[i, j]<>0 then p:=p*a[i, j]; Writeln ( p ); End . Методы доступа к элементам массивов В языке СИ между указателями и массивами существует тесная связь. Например, когда объявляется массив в виде int array[25], то этим определяется не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array, значение которого равно адресу первого по счету (нулевого) элемента массива, т.е. сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array. С точки зрения синтаксиса языка указатель arrey является константой, значение которой можно использовать в выражениях, но изменить это значение нельзя. Поскольку имя массива является указателем допустимо, например, такое присваивание: int array[25]; int *ptr; ptr=array; Здесь указатель ptr устанавливается на адрес первого элемента масcива, причем присваивание ptr=arrey можно записать в эквивалентной форме ptr=&arrey[0]. Для доступа к элементам массива существует два различных способа. Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например, array[16]=3 или array[i+2]=7. При таком способе доступа записываются два выражения, причем второе выражение заключается в квадратные скобки. Одно из этих выражений должно быть указателем, а второе - выражением целого типа. Последовательность записи этих выражений может быть любой, но в квадратных скобках записывается выражение следующее вторым. Поэтому записи array[16] и 16[array] будут эквивалентными и обозначают элемент массива с номером шестнадцать. Указатель используемый в индексном выражении не обязательно должен быть константой, указывающей на какой-либо массив, это может быть и переменная. В частности после выполнения присваивания ptr=array доступ к шестнадцатому элементу массива можно получить с помощью указателя ptr в форме ptr[16] или 16[ptr]. Второй способ доступа к элементам массива связан с использованием адресных выражений и операции разадресации в форме *(array+16)=3 или *(array+i+2)=7. При таком способе доступа адресное выражение равное адресу шестнадцатого элемента массива тоже может быть записано разными способами *(array+16) или *(16+array). При реализации на компьютере первый способ приводится ко второму, т.е. индексное выражение преобразуется к адресному. 15 Для приведенных примеров array[16] и 16[array] преобразуются в *(array+16). Для доступа к начальному элементу массива (т.е. к элементу с нулевым индексом) можно использовать просто значение указателя array или ptr. Любое из присваиваний *array = 2; array[0] = 2; *(array+0) = 2; *ptr = 2; ptr[0] = 2; *(ptr+0) = 2; присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array=2 и *ptr=2, так как в них не требуется выполнять операции сложения. Указатели на многомерные массивы Указатели на многомерные массивы в языке СИ - это массивы массивов, т.е. такие массивы, элементами которых являются массивы. При объявлении таких массивов в памяти компьютера создается несколько различных объектов. Например при выполнении объявления двумерного массива int arr2[4][3] в памяти выделяется участок для хранения значения переменной arr, которая является указателем на массив из четырех указателей. Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес массива из трех элементов типа int, и, следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов. Такое выделение памяти показано на схеме на arr Распределение памяти для двумерного массива.
Таким образом, объявление arr2[4][3] порождает в программе три разных объекта: указатель с идентификатором arr, безымянный массив из четырех указателей и безымянный массив из двенадцати чисел типа int. Для доступа к безымянным массивам используются адресные выражения с указателем arr. Доступ к элементам массива указателей осуществляется с указанием одного индексного выражения в форме arr2[2] или *(arr2+2). Для доступа к элементам двумерного массива чисел типа int должны быть использованы два индексных выражения в форме arr2[1][2] или эквивалентных ей *(*(arr2+1)+2) и (*(arr2+1))[2]. Следует учитывать, что с точки зрения синтаксиса языка СИ указатель arr и указатели arr[0], arr[1], arr[2], arr[3] являются константами и их значения нельзя изменять во время выполнения программы. Размещение трехмерного массива происходит аналогично и объявление float arr3[3][4][5] порождает в программе кроме самого трехмерного массива из шестидесяти чисел типа float массив из четырех указателей на тип float, массив из трех указателей на массив указателей на float, и указатель на массив массивов указателей на float. |