Тема 3.5 Одномерные и многомерные массивы.



Мы поможем в написании ваших работ!


Мы поможем в написании ваших работ!



Мы поможем в написании ваших работ!


ЗНАЕТЕ ЛИ ВЫ?

Тема 3.5 Одномерные и многомерные массивы.



 

    Понятие массива знакомо из Паскаля. Массив — это структура однотипных элементов, занимающих непрерывную область памяти. С массивом связаны следующие его свойства: имя, тип, раз­ мерность, размер. 

   Формат описания массива следующий: тип элементов имя [константное выражение]   Константное выражение определяет размер массива, т. е. числе элементов этого массива. Например, согласно описанию 

int A[10]; 

объявлен массив с именем А, содержащий 10 элементов целого типа. Элементы массива обозначаются индексированными имена­ ми. Нижнее значение индекса равно 0:

А[0], А[1], А[2], А[3], А[4], А[5], А[6], А[7], А[8], А[9]

    В отличие от Паскаля в Си нельзя определять произвольные диапазоны для индексов.

   Размер массива, указанный в описании, всегда на единицу больше максимального значения индекса. Размер массива может явно не указываться, если при его объявлении производится инициализация значений элементов. Например: int p[]={2, 4, б, 10, 1}; В этом случае создается массив из пяти элементов со следующими значениями: р[0]=2, р[1]=4, р[2]=б, р[3]=10, р[4]=1 

   В результате следующего объявления массива int М[б]={5, 3, 2}; будет создан массив из шести элементов. Первые три элемента получат инициализированные значения. Значения остальных будут либо неопределенными, либо равны нулю, если массив внешний или статический. Рассмотрим несколько примеров программ обработки одномерных массивов. Ввод с клавиатуры и вывод на экран одномерного массива.

 

 

 

 

 

   В данной программе массив инициализирован. Его размер равен числу заданных значений. Чтобы сделать программу универсальной по отношению к размеру массива, значение размера вычисляется автоматически и заносится в переменную п. Для этого используется операция sizeof () — определение раз­ мера в байтах. Результат sizeof (X) равен размеру в памяти всего массива х — 20 байтам. Результат sizeof (Х[0] ) равен размеру одного элемента массива — 2 байтам. Отношение этих величин равно 10 — числу элементов массива. Внимательно проанализируйте организацию перебора значений параметров вложенных циклов — i, j .

 

  В результате выполнения этой программы на экран выведется упорядоченная числовая последовательность 

123456789 10

Многомерные массивы. Двумерный массив трактуется как одномерный массив, элементами которого является массив с указанным в описании типом элементов. Например, оператор float R[5][10]; объявляет массив из пяти элементов, каждый из которых есть массив из десяти вещественных чисел. Отдельные величины этого массива обозначаются именами с двумя индексами: R[0] [0], R [ 0 ] [ 1 ], ..., R [ 4 ] [ 9]. Объединять индексы в одну пару скобок нельзя, т.е. запись R[2, 3] ошибочна. Пример описания трехмерного массива: double X[3][7][20];

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

 

   Рассмотрим примеры программ обработки матриц — числовых двумерных массивов. Пример 4. Вычисление и вывод на экран таблицы умножения в форме матрицы Пифагора.

 

                                                     

 

 

  По данной программе в двумерном массиве А не будут заполнены нулевая строка и нулевой столбец. Попрежнему интерпретируем первый индекс двумерного массива как номер строки матрицы, а второй индекс — как номер столбца. 

Пример 5. Заполнение матрицы случайными числами в диапазоне от 0 до 99 и поиск в ней максимального значения.

 

 

В результате тестирования этой программы получен следующий результат:

 

    В данной программе имеются новые элементы, использование которых требует пояснения. В стандартной библиотеке с заголовочным файлом stdlib.h содержится функция, прототип которой имеет вид: int rand (void).   

    Результатом этой функции является целое случайное число из диапазона от 0 до RAND_MAX. Значение константы RAND_MAX определено в заголовочном файле stdlib.h и обычно равно 32767 — максимально допустимому целому числу. Для получения случайных чисел в диапазоне от 0 до N- 1 достаточно вычислить остаток от целого деления rand () на Ж Функция с прототипом void randomize (void) выполняет первоначальную настройку датчика случайных чисел так, чтобы последовательность чисел не повторялась при повторном выполнении программы. 

      Другим новым элементом в данной программе является использование манипуляторов для управления потоковым выводом с помощью стандартного объекта cout. Манипуляторы объявляются в заголовочном файле iomanip.h. Манипулятор setw(n) влияет на формат следующего элемента выходного потока. Он указывает на то, что значение будет выводиться в n позиций на экране (в программе п = б). Другой использованный манипулятор — endl — обозначает конец строки и переводит экранный курсор на начало новой строки. Его действие аналогично действию управляющего символа \n.

Тема 3.6 Указатели.

 

   Понятие указателя знакомо , в котором описывается ссылочный тип данных в Паскале. Смысл этого понятия в Си/Си++ остается тем же: указатель — это адрес поля памяти, занимаемого программным объектом. 

   Пусть в программе определены три переменные разных типов:

 

 

Эти величины разместились в памяти компьютера следующим образом:

 

 

    Операция & — адрес. Применение этой операции к имени переменной дает в результате ее адрес в памяти. Для переменных из  данного выше примера: &а равно FFC0, &с - FFC2, &r - FFC3. 

    Описание указателей. Для хранения адресов используются переменные типа «указатель». Формат описания таких переменных следующий: 

тип *имя_переменной 

     Примеры описания указателей:

int *pti; char *ptc; float *ptf;

  После такого описания переменная pti может принимать значение указателя на величину целого типа; переменная ptc предназначена для хранения указателя на величину типа char; переменная ptf — на величину типа float. 

  Указателям могут присваиваться значения адресов объектов только того типа, с которым они описаны. В нашем примере допустимы операторы pti=&a; ptc=&c; ptf=&r; В результате указатели примут следующие значения:

 

   Как и для других типов данных, значения указателей могут инициализироваться при описании. Например:

 

    В заголовочном файле stdio.h определена константа — нулевой указатель с именем NULL. Ее значение можно присваивать указателю. Например:

Не надо думать, что после этого указатель ptf будет ссылаться на нулевой байт памяти. Нулевой указатель обозначает отсутствие конкретного адреса ссылки. Использованный в описаниях указателей символ * (звездочка) в данном контексте является знаком операции разадресации. С  ее помощью можно сослаться через указатель на соответствующую переменную. После приведенных выше описаний в записи выражений этой программы взаимозаменяемыми становятся а и *pti, с и *ptc, г и *ptf. Например, два оператора х=а+2; И x=*pti+2; тождественны друг другу. В результате выполнения оператора cout<<*pti«a; на экран выведется 55.

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

    Продемонстрируем это правило на определенных выше указателях. Выполнение операторов 

pti=pti+l; ИЛИ pti++; 

     изменит значение указателя pti на 2, в результате чего он примет значение FFC2. В результате выполнения оператора pti—; значение указателя уменьшится на 2 и станет равным FFBE. 

Аналогично для указателей других типов: 

ptc++; увеличит значение указателя на 1;

 ptf++; увеличит значение указателя на 4. 

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

   В следующем примере функция swap () производит обмен значениями двух переменных величин, заданных своими указателями в аргументах.

 

т. е. переменные х и у поменялись значениями.

   Все выглядит очень похоже на то, как если бы в Паскале использовали процедуру обмена с var-параметрами. И тем не менее передача параметров здесь тоже происходит по значению, только этими значениями являются указатели. После обращения к функции указатель а получил адрес переменной х, указатель b — адрес переменной у. После этого переменная х в основной программе и разадресованный указатель *а в функции оказываются связанными с одной ячейкой памяти; так же — у и *Ь. 

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

    Указатели и массивы. Сейчас мы обсудим одно неожиданное обстоятельство в языке Си (неожиданное, с точки зрения человека, изучающего Си после Паскаля). Имя массива трактуется как указатель-константа на массив. Пусть, например, в программе объявлен массив:

  int X[10]; 

В таком случае х является указателем на нулевой элемент массива в памяти компьютера. В связи с этим истинным является отношение

  Х==&Х[0]

   Отсюда следует, что для доступа к элементам массива кроме индексированных имен можно использовать разадресованные указатели по принципу: имя [индекс] тождественно * (имя + индекс) Например, для описанного выше массива х взаимозаменяемы следующие обозначения элементов: 

Х[5], ИЛИ *(Х+5),ИЛИ *(5+Х).

 

   Напоминаем, что для указателей работают свои правила сложения. Поскольку х — указатель на величину целого типа, то Х+5 увеличивает значение адреса на 10. В языке Си символ [ играет роль знака операции сложения адреса массива с индексом элемента массива.

    Из сказанного должно быть понятно, почему индекс первого элемента массива всегда нуль. Его адрес должен совпадать с адресом массива: Х[0]==*(Х+0) 

    Поскольку имя массива является указателем-константой, то его нельзя изменять в программе, т. е. ему нельзя ничего присваивать. Например, если описаны два одинаковых по структуре массива int X[10],Y[10]; то оператор присваивания X=Y будет ошибочным. Такое возможно в Паскале, но недопустимо в Си. Пересылать значения одного массива в другой можно только поэлементно. Теперь рассмотрим двумерные массивы. Пусть в программе при соотствует описание: int P[5][10] ;  Это матрица из пяти строк и десяти чисел в каждой строке. Двумерный массив расположен в памяти в последовательности по строкам. По-прежнему Р является указателем-константой на массив, т. е. на элемент р [ 0 ] [ 0 ] . Индексированное имя р [ i ] обозначает /-ю строку. Ему тождественно следующее обозначение в форме разадресованного указателя: 

*(P+i*10) 

    Обращение к элементу массива р[2] [4] можно заменить на

 *(Р+2*Ю+4) .

 В общем случае эквивалентны обозначения: P[i][j] И *(P+i*10+j) 

       Здесь дважды работает операция «квадратная скобка». Последнее выражение можно записать иначе, без явного указания на длину строки матрицы р: *(*(P+i)+j) .

 Очевидно, что по индукции для ссылки на элемент трехмерного массива A[i] [j] [k] справедливо выражение * (* (* (A+i)+j)+k) и т.д. 

Массив как параметр функции. Обсудим эту тему на примерах. 

    Пример 1. Составим программу решения следующей задачи. Дана вещественная матрица А[м] [N]. Требуется вычислить и вывести евклидовы нормы строк этой матрицы.

   Евклидовой нормой вектора называют корень квадратный из суммы квадратов его элементов:

  Если строку матрицы рассматривать как вектор, то данную формулу надо применить к каждой строке. В результате получим U чисел.  

  Определение функции вычисления нормы произвольного вектора:

 

   В обоих случаях в качестве второго параметра функции используется указатель на начало массива. Во втором варианте это более очевидно, однако оба варианта тождественны. При вызове функции Norma () в качестве второго фактического параметра должен передаваться адрес начала массива (вектора). 

   Рассмотрим фрагмент основной программы, использующей данную функцию для обработки матрицы размером 5x10.

  В обращении к функции второй фактический параметр А [ i ] является указателем на начало /-й строки матрицы А. 

  Пример 2. Заполнить двумерную матрицу случайными целыми числами в диапазоне от 0 до 99. Отсортировать строки полученной Матрицы по возрастанию значений. Отсортированную матрицу вывести на экран.

 

      Здесь все выглядит совсем как при использовании процедур на Паскале. Обратите внимание на прототип и заголовок функции Matr () . В них явно указывается вторая размерность параметра- матрицы. Первую тоже можно указать, но это необязательно. Как уже говорилось выше, двумерный массив рассматривается как одномерный массив, элементами которого являются массивы (в данном случае — строки матрицы). Компилятору необходимо «знать»     

 

размер этих элементов. Для массивов большей размерности (3, 4 и т.д.) в заголовках функций необходимо указывать все размеры, начиная со второго.

      При обращении к функции Matr () фактическим параметром является указатель на начало двумерного массива А, а при обращении к функции Sort О — указатели на начало строк. В итоге тестирования программы получен следующий результат. 

Матрица до сортировки:

 

Список литературы .

Основные источники:

1. Белов В.В., Чистякова В.И. Программирование в Delphi: процедурное, объектно-ориентированное, визуальное. Учебное пособие. – М.: Горячая линия – Телеком, 2009

2. Голицына О.Л., Попов И.И. Основы алгоритмизации и программирования. – Форум, 2010

3. Семакин И.Г., Шестаков А.П. Основы алгоритмизации и программирования. – М.: Издательский центр «Академия», 2008

4. Семакин И.Г., Шестаков А.П. Основы программирования. – М.: Издательский центр «Академия», 2008

Дополнительные источники:

1. Желонкин А. Основы программирования в интегрированной среде Delphi. Практикум – М.: Бином. Лаборатория знаний, 2006

2. Павловская Т. А. Паскаль. Программирование на языке высокого уровня. Учебник – Питер, 2010 г.

3. Павловская Т.А., Щупак Ю. А. C/C++. Структурное и объектно-ориентированное программирование. Практикум. Учебное пособие – Питер, 2011

4. Павловская Т.А., Щупак Ю.А. C++. Объектно-ориентированное программирование. Практикум. – Питер, 2008 г.

5. Павловская Т.А. Паскаль. Программирование на языке высокого уровня. Практикум. – Питер, 2007

 

                                                                                                                                         



Последнее изменение этой страницы: 2021-04-05; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.237.38.244 (0.01 с.)