
Общие сведения
В программировании используется следующее определение массива: массив - это совокупность однотипных элементов данных.
Массив - не простой тип данных, а структурированный, т.е. имеет более чем один компонент и построен на основе других типов. Компоненты массива называют элементами. Все элементы массива имеют общее имя. Обращение к конкретному элементу производится с помощью индекса. Важной отличительной чертой массива является однотипность элементов массива. В качестве типа можно использовать любой простой тип или структурированный (т. е. возможна вложенность типов). В C++ количество элементов в массиве фиксировано и определяется во время трансляции. Это является недостатком языка, поскольку не во всех задачах можно заранее определить количество элементов в массиве (возможность использования динамических массивов рассматривается в разд. 4.4).
Обработка одномерных массивов
Одномерные массивы имеют аналогию с таким понятием в математике, как вектор. Описание переменной типа массив задается следующим образом:
<тип> <имя массива> [<t>];
Здесь <тип> - тип элементов массива (любой тип языка C++);
<имя массива> — правильный идентификатор;
<t> - количество элементов массива;
Ниже приведены примеры описания одномерных массивов:
int f [10];
58
float b [5]
В языке программирования C++ индексы элементов массива изменяются от 0. В этом случае индекс последнего элемента на единицу меньше количества элементов в массиве. Размерность массива - величина произвольная, однако суммарная длина внутреннего представления любого массива не может быть больше 65520 байт. Так как память выделяется под массив во время компиляции программы, границы изменения индексов должны быть константами, или константными выражениями.
Обращение к элементам массива осуществляется по их индексам:
f [1] =0;
mas [100] ='a';
b [3]=1.13;
При обработке массивов возникают такие задачи, как ввод элементов массива, нахождение суммы, произведения, среднего и т. д., поиск некоторого элемента в массиве, сортировка элементов массива, вывод элементов массива.
На рис.17 приведена схема алгоритма формирования элементов массива с помощью датчика случайных чисел, вывод элементов массива на экран, вычисление суммы всех элементов. Программа приведена в примере pr18.
// Пример pr18
#include <stdio.h> #include <conio.h>
#include <stdlib.h>
void main()
{
const int=100; // Максимальный размер массива
int a[N1],
i, // Индекс элемента массива
n,s; // Количество элементов массиве и их сумма
printf("\nВведите число элементов массива:");
scanf("%d",&n);
// Форматирование массива с помощью датчика случайных чисел
randomize(); // Инициализация датчика случайных чисел
for (i=0; i<n; i++) a[i]=random(10); printf("Полученный массив: \n");
for (i=0; i<n; i++)
printf("%5d",a[i]);
s=0; // Нахождение суммы
for (i=0; i<n; i++)
s=s+a[i];
printf("\ns=%d",s);
59
Рис. 17
60
В приведенном примере в качестве исходных данных вводится размер массива. Константа N1 делает программу более универсальной, позволяя работать с целочисленными массивами, размерность которых может изменяться от 1 до 100. Если будет введено число, меньшее 1 и большее 100, то возникнет ошибка. В C++ принято для идентификаторов констант использовать заглавные буквы. Для формирования массива (во всех случаях, когда требуется перебор всех элементов массива) лучше всего подходит оператор цикла со счетчиком. В каждой итерации оператора цикла очередному элементу массива (индекс элемента является переменной цикла) присваивается псевдослучайное число. Результатом работы программы является сформированный массив и сумма элементов этого массива.
Аналогично решается задача нахождения произведения элементов массива, только начальное значение для произведения задается равным 1 и знак "+" меняется на знак "*".
На рис. 18 приведена графическая схема алгоритма определения максимального элемента массива и произведения положительных элементов, а также замены максимального элемента массива произведением положительных элементов массива. Эта же задача решается в программе рr19.
// Пример рr19
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main()
{
const int N1 =100; // Максимальный размер массива
int a[N1], i, // Индекс элемента массива
n,р, // Количество элементов в массиве и их произведение
imax; // Индекс максимального элемента
printf("\nВведите число элементов массива:");
scanf("%d",&n);
printf("Введите элементы массива");
for (i=0; i<n; i++)
scanf("%d",&a[i]);
p=1;
imax=0; // Предполагаем, что первый элемент максимальный}
for (i=0; i<n; i++)
{
// Если элемент положительный, то умножаем нашего р
if (a[i]>0) p=p*a[i];
// Если текущий элемент массива больше
// максимального, то запоминаем его индекс
i
61
62
}
printf("\n Максимальный элемент массива =%d",a[imax]);
a[imax]=p; //Замена максимального элемента, произведением
printf("\np=%d",p); printf("\n Обработанный массив:");
for (i=0; i<n; i++)
printf("%5d",a[i]); getch();
}
В дальнейшем, в схемах алгоритма подробно изображать ввод и вывод массива не будем, чтобы алгоритм был нагляднее.
В приведенном примере массив вводится с клавиатуры. Дня ввода массива используется оператор цикла со счетчиком. За начальное значение для индекса максимального элемента берем 0, т. е. предполагаем, что первый элемент максимальный. Далее в цикле перебираются see элементы массива и сравниваются с нулем для того, чтобы умножать или не умножать на элемент. В этом же цикле каждый элемент сравнивается с a[imax] для выяснения, не встретился ли элемент, больший прежнего максимального, и если встретился, то запоминается его индекс, чтобы в следующий раз сравнивать текущий элемент с большим из перебранных. В условном операторе if a[i]>a[imax] ветвь else отсутствует; это означает, что в случае невыполнения условия imax- остается без изменения, что и обеспечивает наличие в области памяти с идентификатором imах значение индекса максимального из перебранных элементов.
На рис. 19 приведена графическая схема алгоритма сортировки элементов одномерного массива "методом пузырька" в порядке не убывания. Суть этой сортировки состоит в том, что сравниваются два соседних элемента; если они расположены в порядке убывания, то меняются местами, что фиксируется в переменной fl. После сравнения всех элементов массива принимается решение по состоянию fl об очередном прохождении по массиву. Решение этой задачи реализуется в программе рr20.
// Пример рr20
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main()
{ const int N1=100; // максимальный размер массива
int i, // индекс элемента массива
n,р,// Количество элементов в массиве и их произведение
fl; // Флаг перестановок float a[N1],d; // Дополнительная переменная для обмена
// местами двух элементов массива
63
Рис. 19
scanf("%d",&n);
printf("Bведитe элементы массива: \n");
for (i=0; i<n; i++)
{
printf("Bведите %d-й элемент массива:",i+1);
scanf("%f ",&a[i]);
64
printf("\nИсходный массив: \n");
for (i=0; i<n; i++)
printf("%7.2f ",a[i]);
// Сортировка
do // Повторить
{ fl=1; // Флаг поднять
// В очередной раз просматриваем элементы массива
for (i=0; i<n-l; i++);
if (a[i]>a[i+1]) // Сравниваем два соседних элемента
{ //меняем местами соседние элементы
d=a[i]; a[i]=a[i+1]; a[i+1]=d; fl=0; // если был обмен, то флаг опускаем
} } while (!fl); // Если флаг не опускался, то массив отсортирован
рrintf("\nОбработанный массив: \n");
for (i=0; i<n; i++)
printf("%7.2f ",a[i]);
getch();
}
Основной цикл do while прекращает выполняться, когда значение переменной/7 остается равной 1 после выполнения вложенного цикла for . Это происходит в том случае, если ни одну пару элементов не удается переставить, что указывает на то, что все элементы стоят на своих, местах.
Массивы и указатели
Известно, что данные хранятся в ячейках памяти компьютера. Все ячейки памяти пронумерованы. Номера ячеек памяти называются адресами. Указатели используются для работы с адресами. Указатель - это некоторое символическое представление адреса. Это означает, что мы будем работать с переменными, хранящими эти адреса. Описываются такие переменные следующим образом:
<тип> *<идентификатор>
Такое описание синтаксически отличается от описания простой переменной только наличием знака * перед именем переменной. Как видно из описания, указатель всегда связывается с переменной какого-то определенного типа. Это позволяет определить, сколько байт памяти необходимо выделить по указанному адресу. В переменной типа указатель хранится адрес первого байта, выделенного участка памяти: int *ptr,*ptr1;
f
65
Над указателями можно выполнять следующие операции: Одному указателю можно присвоить значение другого указателя, если они ссылаются на один и тот же тип:
ptr=ptr1; p1=p; Значение указателя можно увеличить или уменьшить на константную величину:
ptr ++;
ptr1 = ptr1-5;
p1 = p1+2;
На самом деле ptr увеличивается не на 1, а на столько, сколько байт занимает целое число. Переменная ptr1 уменьшается на 5 умноженное на количество байт, выделяемых под целое число.
Указателю можно присвоить значение адреса. Для получения адреса используется знакомый значок &:
int a, *ptr;
рtr = &a;
Можно использовать операцию косвенной адресации. Эта операция обозначается значком * и позволяет получить доступ к значению переменной, на которую ссылается указатель:
int n=12, *ptr,a;
ptr = &n;
a = *ptr;
После выполнения этого фрагмента а будет иметь значение 12.
Можно получить разность двух указателей. Это используется чаще всего для указателей, ссылающихся на один и тот же массив.
Здесь следует обратить внимание на то, что символы * и & имеют разное назначение в зависимости от контекста, в котором они используются.
Очень часто в C++ при paботе с массивами вместо индексов используются указатели. Преимущество использования указателей в массивах состоит в том, что арифметические операции над указателями выполняются быстрее, если мы работаем с подряд идущими элементами массива. Если выбор элементов случайный, то быстрее и более наглядна работа с индексами. Имя массива является указателем на первый элемент массива. Элементы массива располагаются в непрерывной области памяти. Эти два положения позволяют перемещаться по элементам массива, увеличивая указатель массива на 1. Пусть
int a[10], *ptr;
ptr = a;
тогда к пятому элементу массива можно обратиться :
а[4]
*
66
*(ptr+4) и все эти обращения будут равносильны.
Напишем программу из примера рr18 с использованием указателей. // Пример pr18 с указателями
#include <stdio.h> #include <conio.h>
#include <stdlib.h>
void main()
{
const int=100; // Максимальный размер массива
int a[N1], *ptr, // Указатель на элементы массива
n,s; // Количество элементов в массиве и их сумма
printf("\nВведите число элементов массива:");
scanf("%d",&n);
// Формирование массива с помощью датчика случайных чисел
randomize(); // Инициализация датчика случайных чисел
for (ptr=а; ptr-a<n; ptr++) *ptr=random(10);
рrintf("Полученный массив: \n");
for (ptr=a; ptr-a<n; ptr++);
printf("%5d",*ptr); s=0; // Нахождение суммы
for (ptr=a; ptr-a<n; ptr++);
s=s+*ptr;
printf("\ns=%5d",s);
getch();
}
Начинающие работать с указателями самую большую сложность испытывают при использовании операции косвенной адресации. Поэтому обращаем особое внимание на эту проблему. Например, в заголовке первого же оператора цикла записано prt = а. Это означает, что переменной типа указатель присваивается значение адреса первого элемента массива а. В теле цикла записано *ptr = random(10). В этом случае переменной, хранящейся по адресу, на который указывает ptr, присваивается значение, полученное с датчика случайных чисел.
Составим программу с использованием указателей для примера рr19.
// Пример pr19 с указателями
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
void main()
{
const int N1 =100; // Максимальный размер массива
67
n,р, // Количество элементов в массиве и их произведение
*ptrmax; // Указатель на максимальный элемент
printf("\nВведите число элементов массива:");
scanf("%d",&n);
printf("Введите элементы массива");
for (ptr=a; ptr-a<n; ptr++)
scanf("%d",ptr);
p=1;
ptrmax=a; // Предполагаем, что первый элемент максимальный
for (ptr=a; ptr-a<n; ptr++)
{
// Если элемент положительный, то умножаем нашего р
if (*ptr>0) p=p**ptr;
// Если текущий элемент массива больше
// максимального, то запоминаем его индекс
if (*ptrmax<*ptr) ptrmax=ptr;
}
printf("\n Максимальный элемент массива =%d",*ptrmax);
*ptrmax=p; // Замена максимального элемента, произведением
printf("\np=%d",p); printf("\n Обработанный массив:");
for (ptr=a; ptr-a<n; ptr++)
printf("%5d",*ptr); getch();
}
Контрольные вопросы
1. Дайте определение массива.
2. Чем отличается индекс массива от элемента массива?
3. Чем отличается N1 от n в приведенных примерах программ?
4. Составьте часть схемы алгоритма, соответствующую следующему фрагменту программы: for (i=0; i<n; i=i+2) k=k*a[i];
5. Дайте определение указателя.
6. Как описываются и какие операции можно выполнять над указателями?
7. Какую роль в этом разделе программы сортировки одномерного массива играет переменная fl?
8. Напишите программу, которая сортирует одномерный массив следующим образом; находит наименьший элемент и меняет его местами с первым элементом массива. Среди всех, кроме первого, снова находит наименьший и ставит его на второе место и т д.
68
Варианта заданий
Во всех предложенных задачах в качестве исходных данных предлагается одномерный массив А размерностью N.
1. Заменить максимальный элемент массива средним арифметическим положительных элементов массива.
2. Заменить максимальный элемент массива средним геометрическим положительных элементов массива.
3. Заменить минимальный элемент массива средним геометрическим положительных элементов массива.
4. Заменить минимальный элемент массива средним арифметическим положительных элементов массива.
5. Заменить минимальный элемент массива максимальным элементом.
6. Поменять местами минимальный и максимальный элементы массива.
7. Поменять местами минимальный и первый отрицательный элементы массива.
8. Поменять местами минимальный и последний отрицательный элементы массива.
9. Поменять местами максимальный и последний отрицательный элементы массива.
10. Поменять местами максимальный и первый отрицательный элементы массива.
11. Определить количество отрицательных элементов массива, предшествующих первому положительному.
12. Определить количество положительных элементов массива, предшествующих первому отрицательному.
13. Заменить все нулевые элементы, предшествующие первому отрицательному, единицей.
14. Вычислить сумму отрицательных, произведение положительных и количество нулевых элементов массива с нечетными индексами.
15.
Сформировать одномерный массив [Bi]
из
элементов
массива [Ai]
по
закону
.
16. Из одномерного массива [Ai] сформировать одномерный массив [Вi], записав в него сначала элементы массива А, имеющие четные индексы, потом - элементы с нечетными индексами.
17. Заменить все наибольшие элементы массива значением минимального элемента.
1
69
19. Заменить нулевые элементы массива суммой элементов, значения которых кратны некоторому целому значению х (х, как и массив, вводится с клавиатуры).
20. Определить порядковый номер того элемента массива, который наиболее близко к некоторому целому числу х (х, как и массив, вводится с клавиатуры).
21. Определить количество чисел в наиболее длинной последовательности из подряд идущих нулей.
22. Определить, сколько элементов массива принимает наибольшее значение.
23.
Кроме массива А, в этой задаче дан
одномерный массив В. Рассматривая
пары чисел
и
,
как координаты точек на плоскости,
определить радиус наименьшего круга
(с центром в начале координат), внутрь
которого попадают все эти точки.
24. Вычислить математическое ожидание М и дисперсию D.
25. Найти три наибольших элемента массива.
70
ЛАБОРАТОРНАЯ РАБОТА №5
ОБРАБОТКА ДВУМЕРНЫХ МАССИВОВ
Цель работы:
приобретение навыков обработки двумерных массивов;
составление алгоритмов и программ, использующих сложные циклы.
Порядок выполнения работы:
В соответствии с поставленной задачей необходимо разработать графическую схему алгоритма, составить программу и отладить ее в среде С++, продемонстрировать преподавателю, подготовить отчет, ответить на контрольные вопросы и защитить лабораторную работу перед преподавателем.