Практическая работа №4. Работа с массивами



Цель работы: изучение принципов работы с массивами в С++.

 

Задачи:

1. Изучить теоретический материал

2. Написать программу согласно индивидуальному варианту задания.

3. Ответить на контрольные вопросы.

4. Оформить отчет.

 

Теоретический материал

Массив представляет собой именованную группу элементов одного типа. Описание массива отличается от описания простой переменной наличием после имени квадратных скобок, в которых задается количество элементов массива (размерность). Элементы массива нумеруются с 0. Массивы могут иметь те же типы данных и классы памяти, что и простые переменные, и к ним применим тот же принцип умолчания.

Рассмотрим примеры, различных описаний массивов:

// несколько описаний массивов

int temp[365]; // внешний массив из 365 целых чисел

main( ) {

float rain[365]; // автоматический массив из 365 чисел типа floaеt

static char code[12]; // статический массив из 12 символов

extern mas[25 ]; // внешний массив

 

Инициализация массивов

Внешние и статические массивы можно инициализировать. Автоматические и регистровые массивы инициализировать нельзя. Прежде чем попытаться инициализировать массив, давайте посмотрим, что там находится, если мы в него ничего не записали.

// проверка содержимого массива

main() {

int mas1[2]; // автоматический массив

static int mas2[2]; // статический массив

printf("%d%d\n", mas1[1], mas2[1]);

}

Результат:

525 0

Если ничего не засылать в массив перед началом работы с ним, то внешние и статические массивы инициализируются нулем, а автоматические и регистровые массивы содержат какой-то «мусор», оставшийся в этой части памяти. Инициализировать массивы можно таким образом:

// дни месяца

int days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

 

main() {

…………

}

Массив определен вне тела функции , поэтому является внешним. Он инициирован списком, заключенным в скобки. Количество элементов в списке должно соответствовать размеру массива.

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

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

Динамические массивы

Если до начала работы программы неизвестно, сколько в массиве элементов, следует использовать динамические массивы. Их создают с помощью оператора new, при этом необходимо указать тип и размерность, например:

int n=100;

float *p=new float [n];

В этой строке создается указатель на float, которому присваивается адрес начала непрерывной области динамической памяти, достаточной для размещения 100 элементов вещественного типа. Адрес ее начала записывается в указатель р. Динамические массивы нельзя при создании инициализировать, и они не обнуляются.

Преимущество динамических массивов состоит в том, что размерность может быть переменной, то есть объем памяти, выделяемой под массив, определяется на этапе выполнения программы. Доступ к элементам динамического массива осуществляется точно так же, как и к статическим. Например, к пятому элементу можно обратиться как p[5] или *(p+5). Рассмотрим подробнее второе обращение. В переменной–указателе p хранится адрес начала массива. Для получения адреса пятого элемента к этому адресу прибавляется смещение 5. Операция сложения с константой для указателей учитывает размер адресуемых элементов, то есть на самом деле индекс умножается на длину элемента массива: p+5*sizeof(float). Затем с помощью операции разадресации * выполняется выборка значения из указанной области памяти.

Если динамический массив перестает быть нужным программе, и мы хотим освободить память, это делается с помощью оператора delete[].

Двумерные массивы

Двумерный массив представляется в С++ как массив, состоящий из массивов. Для этого при его описании в квадратных скобках указывается вторая размерность.

int a[3][5];  // Целочисленная матрица из 3 сток и 5 столбцов.

Массив хранится по строкам в непрерывной области памяти.

a00 a01 a02 a031 a04 a10 a11 a12 a13 a14 a20 a21 a22 a23 a24

0-я строка 1- я строка 2-я строка

Строки массива ничем не отделены одна от другой, то есть прямоугольной матрицей двумерный массив является только в нашем воображении. В памяти сначала располагаетcя одномерный массив а[0], представляющий собой первую строку массива а, затем массив а[1] и т.д.

При просмотре массива от начала в первую очередь изменяется правый индекс (номер столбца). Для доступа к отдельному элементу массива указываются его индексы, например, a[i][j], где i и j – выражения целочисленного типа (или более экзотическим способом: *(a[i]+j)).

При инициализации двумерного массива он представляется либо как массив из массивов, при этом каждый массив заключается в свои фигурные скобки (в этом случае левую размерность при описании можно не указывать), либо задается общий список элементов в том порядке, в котором элементы располагаются в памяти:

int mass2 [ ][2]={{1,1}, {0,2), {1,0} };

int mass2 [3][2]={1, 1, 0, 2, 1, 0};

Для создания динамического многомерного массива необходимо указать в операции new все его размерности (самая левая размерность может быть переменной), например:

int nstr=5;

int ** m=(int **) new int[nstr][10];

Более универсальный и безопасный метод выделения памяти под двумерный массив, когда обе его размерности задаются на этапе выполнения программы, приведен ниже:

int nstr, nstb;

cout << "Введите количество строк и столбцов: ";

cin >> nstr >> nstb;

int **a=new int *[nstr] //1

for (int I=0; I<nstr; I++) //2

a[i]=new int [nstb]; //3

В операторе 1 объявляется переменная типа «указатель на указатель на int» и выделяется память под массив указателей на строки массива. В операторе 2 организуется цикл для выделения памяти под каждую строку массива. В операторе 3 каждому элементу массива указателей на строки присваивается адрес начала участка памяти, выделенного под строку двумерного массива. Каждая строка состоит из nstb элементов типа int.

Освобождение памяти из-под массива с любым количеством измерений выполняется с помощью операции delete[].

10.4 Указатели массивов

Имя массива определяет также его первый элемент, т. е. если mas[ ] — массив, то mas = &mas[0], и обе части равенства определяют адрес первого элемента массива. Оба обозначения являются константами типа указатель, поскольку они не изменяются на протяжении всей программы. Однако их можно присваивать переменной типа указатель и изменять значение переменной, как показано в нижеследующем примере.

// прибавление к указателю

main() {

int dates[4], *pti, index;

float bills[4], *ptf:

pti = dates; // присваивает адрес указателю массива

ptf = bills;

for (index = 0; index < 4; index++)

   printf(" указатели + %d: %10u %10u\n", index, pti + index, ptf + index);

}

Вот результат:

указатели +0: 56014 56026

указатели +1: 56016 56030

указатели +2: 56018 56034

указатели +3: 56020 56038

Первая напечатанная строка содержит начальные адреса двух массивов, а следующая строка — результат прибавления единицы к адресу и т. д. Почему так получается?

56014 + 1 =56016

56026 + 1=56030

В нашей системе единицей адресации является байт, но тип int использует два байта, а тип float — четыре. При прибавлении единицы к указателю компилятор языка Си добавит единицу памяти. Для массивов это означает, что мы перейдем к адресу следующего элемента, а не следующего байта. Вот почему мы должны специально оговаривать тип объекта, на который ссылается указатель.

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

dates + 2 = = &dates[2] /*один и тот же адрес */

*(dates + 2) = = dates[2] /* одно и то же значение "/

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

Нужно различать выражения *(dates + 2) и *dates + 2. Операция (*) имеет более высокий приоритет, чем +, поэтому последнее выражение означает (* dates) + 2.

*(dates + 2) /* значение 3-го элемента массива dates */

dates + 2 /* 2 добавляется к значению 1-го элемента массива */

Задача 1. Количество элементов между минимумом и максимумом.

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

 

Для экономии времени при отладке значения элементов массива задаются путем инициализации.

 

#include <iostream.h>

 int main(){

const int n=10;

int a[n]={1,3,-5,1,-2,1,-1,3,8,4};

int I,imax,imin,count;

for (I=imax=imin=0; I<n; I++){

    if (a[I]>a[imax]) imax=I;

    if (a[I]<a[imin]) imin=I;

}

cout <<"\n\ max=" <<a[imax]<<" min="<< a[imin]; // Отладка

int ibeg=imax<imin ? imax : imin;

int iend=imax<imin ? imax : imax;

cout << "\n\t ibeg=" << ibeg<< " iend=" <<iend; // Отладка

for (count=0,I=ibeg+1; I<iend; I++)

if (a[I]>0) count++;

cout <<"\n Количество положительных элементов:"<< count << endl;

return 0;

 }

 

После нахождения каждой величины вставлена отладочная печать. Не пренебрегайте этим способом отладки и не жалейте времени.

Основные моменты при работе с одномерными массивами.

· Размерность нединамического массива может быть только константой или константным выражением. Рекомендуется задавать размерность с помощью именованной константы.

· Элементы массива нумеруются с нуля, поэтому максимальный номер элемента всегда на единицу меньше размерности.

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

· Указатель – это переменная, в которой хранится адрес области памяти.

· Имя массива является указателем на его нулевой элемент.

· Обнуления динамической памяти при ее выделении не происходит. Инициализировать динамический массив нельзя.

· Освобождение памяти, выделенной посредством new(), выполняется с помощью операции delete().

· Перед выходом локального указателя из области его действия необходимо освобождать связанную с ним динамическую память.

· Если количество элементов, которые должны быть введены в программу, известно до ее выполнения, определяйте массив в операторе описания переменных (причем лучше как локальную переменную, чем глобальную); если количество можно задать во время выполнения программы, но до ввода элементов, создавайте динамический массив; если нет, используйте линейный список или другую динамическую структуру.

· Алгоритмы сортировки массивов различаются по быстродействию, занимаемой памяти и области применения.

 

Задача 2. Среднее арифметическое и количество положительных элементов

Написать программу, которая для целочисленной матрицы 3x4 определяет среднее арифметическое ее элементов и количество положительных элементов в каждой строке.

 

#include <iostream.h>

 #include <iomanip.h>

 void main(){

const int nrow=3,ncol=4;

int a[nrow][ncol];

int i,j;

cout<<"Введи элементы массива:" << endl;

for (i=0; i<nrow; i++)

    for (j=0; j<ncol; j++) cin >> a[i][j];

for (i=0; i<nrow; i++){

    for (j=0; j<ncol; j++) cout << setw(4) << a[i][j] << " ";

    cout << endl;

}

int n_pos_el;

float s=0;

for (i=0; i<nrow; i++) {

    n_pos_el=0;

   for (j=0; j<ncol; j++) {

        s+=a[i][j];

        if (a[i][j]>0) n_pos_el++;

    }

    cout << " Строка: " << i << "кол-во пол. Элементов: " << n_pos_el << endl;

}

s/=nrow*ncol;

cout << " Среднее арифметическое: " << s << endl;

 }

 

После ввода значений элементов предусмотрен их контрольный вывод на экран. Для того, чтобы элементы матрицы располагались один под другим, используется манипулятор setw(), устанавливающий для очередного выводимого значения ширину поля в 4 символа. Для использования манипулятора необходимо подключить заголовочный файл <iomanip.h>. После каждой строки выводится символ перевода строки endl.

 

Основные моменты при работе с двумерными массивами.

· В массивах, определенных с помощью операторов описания, обе размерности должны быть константами или константными выражениями.

· Массив хранится по строкам в непрерывной области памяти.

· Первый индекс – номер строки, второй – номер столбца. Каждый индекс может изменяться от 0 до значения соответствующей размерности, уменьшенной на единицу.

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

· При выделении динамической памяти сразу под весь массив самую левую размерность можно задавать с помощью переменной, все остальные размерности должны быть константами. Для двумерного массива это означает, что первая размерность может быть константой или переменной, а вторая – только константой.

· Для выделения динамической памяти под массив, в котором все размерности переменные, используются циклы.

· Освобождение памяти из-под массива с любым количеством измерений выполняется с помощью операции delete()


Дата добавления: 2020-04-25; просмотров: 202; Мы поможем в написании вашей работы!

Поделиться с друзьями:






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