Структуры и алгоритмы компьютерной обработки данных

Массивы: одномерные массивы

Показывать лекцию целиком

Цель лекции: изучить понятия, особенности внутреннего представления, способов генерации и вывода одномерных массивов, научиться выполнять объявление, инициализацию, генерацию и вывод массивов при решении задач на языке C++.

В программировании часто возникают задачи, связанные с обработкой больших объемов данных. Для того, чтобы весь этот объем данных хранить внутри программы, применяют массивы – простейшую разновидность структурированных типов данных.

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

(рис 10.1) Схематическое представление массивов

Простейшим аналогом двумерного массива может быть таблица, а трехмерного – несколько таблиц одинакового размера. Математические объекты типа вектор и матрица – примеры аналогов (соответственно одно- и двумерных) массивов.

Тип элемента массива может быть одним из базовых (скалярных), типом другого массива, типом указателя, типом структуры или объединения.

Элементы массива в С++ нумеруется, начиная с нуля. У одномерных массивов после его имени указывается один индекс (порядковый номер), заключенный в прямоугольные скобки [ ], а у многомерных – несколько, каждый из которых заключается в [ ]. Последнее означает, что многомерный массив создается путем определения массива из элементов типа массив.

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

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

Объявление одномерных массивов

Синтаксис определения массива без дополнительных спецификаторов и модификаторов имеет два формата:

Тип ИмяМассива[ВыражениеТипаКонстанты];

или

Тип ИмяМассива[];

ИмяМассива – идентификатор массива.

Тип – тип элементов объявляемого массива. Элементами массива не могут быть функции, файлы и элементы типа void.

ВыражениеТипаКонстанты – задает количество элементов (размерность) массива. Выражение константного типа вычисляется на этапе компиляции. Данное константное выражение может быть опущено в случаях если:

  • при объявлении массив инициализируется;
  • массив объявлен как формальный параметр функции;
  • массив объявлен как ссылка на массив, явно определенный в другом файле.
  • Например:

  • int a[100]; //массив из 100 элементов целого типа
  • double d[14]; // массив из 14 элементов типа double
  • char s[]="Программирование"; //символьный массив
  • const int t=5, k=8;
    float wer[2*t+k]; 
    //массив из 2*t+k элементов вещественного типа
  • int sample[853]; 
    /*массив из элементов sample[0], sample[1], 
      sample[2],...,sample[852] типа int*/
    равносильно объявлению
    const int N_max=853;
    int sample[N_max];
    равносильно объявлению
    #define N_max 853
    ...
    int sample[N_max];
  • В языке С++ не производится проверки границ массивов: таким образом, исполнение кода не остановится при выходе за границы массива. Если переполнение массива происходит во время выполнения оператора присваивания, то лишние значения могут присвоиться другим переменным или включиться в текст программы. С другой стороны, можно объявить массив размером N и указать индекс элемента, выходящий за пределы N, что не приведет к появлению сообщений об ошибке, как на шаге компиляции, так и на шаге выполнения, даже если это послужит причиной аварийного завершения программы.

    Инициализация одномерных массивов

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

    В С++ одновременно с объявлением массива можно задать начальные значения всех элементов массива или только нескольких первых его компонент.

    Например:

    float t[5]={1.0, 4.3, 8.1, 3.0, 6.74};
    char b[7]={'П','р','и','в','е','т'};
    /*в данных примерах длину массива компилятор вычисляет по 
    количеству начальных значений, перечисленных в фигурных 
    скобках*/
    int d[10]={1, 2, 3};
    char a[10]="Привет";
    /*в данных примерах определяется значение только заданных 
    переменных d[0],d[1],d[2] и a[0],a[1],...,d[9], остальные 
    элементы не инициализируются*/

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

    Пусть необходимо проинициализировать массивы для создания таблицы сообщений об ошибках:

    char e1[12] = "read error\n";
    char e2[13] = "write error\n";
    char e3[18] = "cannot open file\n";

    Компилятор С++ сам сформирует нужное значение по количеству инициализирующих данных. В нашем случае под массив e2 будет отведено 13 байтов, включая последний байт с нулевым кодом, завершающий каждую строку. Оператор

    printf("%s имеет длину, равную %d\n",e2,sizeof (e2));

    выведет на экран

    write error
    имеет длину, равную 13

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

    Обращение к элементам одномерного массива

    Адресация элементов массива осуществляется с помощью индексированного имени. Синтаксис обращения к элементу массива:

    ИмяМассива[ВыражениеТипаКонстанты];

    или

    ИмяМассива[ЗначениеИндекса];

    Обращаться к элементам массива можно также посредством механизма указателей.

    Таким образом, чтобы обратиться к элементу массива, надо указать имя массива и номер элемента в массиве (индекс).

    Например:

    a[0] – индекс задается как константа,

    d[55] – индекс задается как константа,

    s[i] – индекс задается как переменная,

    w[4*p] – индекс задается как выражение.

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

    Определение размера памяти для одномерных массивов

    Массив занимает непрерывную область памяти. Для одномерного массива полный объем занимаемой памяти в байтах вычисляется по формуле:

    Байты = sizeof (тип) * размер массива

    Массив представляет собой набор однотипных данных, расположенных в памяти таким образом, чтобы по индексам элементов можно было легко вычислить адрес соответствующего значения. Например, пусть одномерный массив A состоит из элементов, расположенных в памяти подряд по возрастанию индексов, и каждый элемент занимает по k байт. Тогда адрес i -того элемента вычисляется по формуле:

    адрес(A[i]) = адрес(A[0]) + i*k

    Пример 1. Определение размера памяти одномерного массива.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    #define v 4
    #define p 3
    
    int _tmain(int argc, _TCHAR* argv[]){
      const int q=4, r=1;
      int i_mas[10]; 
      int k=sizeof(i_mas);
      cout << "i_mas[10] занимает " << k << " байт\n";
      
      float f_mas[7]={2.0,4.5,8.3,7.0,1.0};
      int t=sizeof(f_mas);
      cout << "f_mas[7]={2.0,4.5,8.3,7.0,1.0} занимает "<< t <<"байт\n";
    
      double d_mas[2*q-r];
      int w=sizeof(d_mas);
      cout << "d_mas[2*q-r] занимает " << w << " байт\n"; 
    
      double d1_mas[2*v/p];
      int w1=sizeof(d1_mas);
      cout << "d1_mas[2*v/p] занимает " << w1 << " байт\n";  
    
      char c_mas[]="Программирование";
      int s=sizeof(c_mas);
      cout << "c_mas[]=\"Программирование\"занимает"<< s <<"байт\n";
    
      system("pause");
      return 0;
    }

    Результат выполнения программы:

    i_mas[10] занимает 40 байт – 4 байта (тип int ) * 10 (количество элементов массива)

    f_mas[7] = {2.0,4.5,8.3,7.0,1.0} занимает 28 байт – 4 байта (тип float ) * 7 (объявленное количество элементов массива)

    d_mas[2*q-r] занимает 56 байт – 8 байт (тип double ) * 7 (вычисленное через формулу количество элементов массива)

    d1_mas[2*v/p] занимает 16 байт – 8 байт (тип double ) * 2 (вычисленное через формулу количество элементов массива)

    c_mas[]="Программирование" занимает 17 байт – 1 байт (тип char ) * 17 (16 знаков + нулевой байт '\0' )

    Указатели и одномерные массивы

    В языке С++ между указателями и массивами существует тесная связь. Например, когда объявлен массив int array[25], этим определяет не только выделение памяти для двадцати пяти элементов массива, но и для указателя с именем array. В языке С++ имя массива без индексов трактуется как адрес начального элемента. То есть имя массива является указателем на массив. Таким образом, доступ к элементам массива осуществляется через указатель с именем array.

    Поскольку имя массива является указателем, допустимо, например, такое присваивание:

    int arrаy[25];
    int *ptr;
    ptr=array;

    Здесь указатель ptr устанавливается на адрес первого элемента массива, причем присваивание ptr = arrаy можно записать в эквивалентной форме ptr=arrаy[0].

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

    Адрес в памяти 1000 1002 1004 1008 1008 ...
    Индекс 0 1 2 3 4 ... 24
    Значения 1 2 3 4 5 ... 25
    Адрес i - го элемента
    array
    array[0]
    ptr
    array +1
    array[1]
    ptr + 1
    array +2
    array[2]
    ptr + 2
    array +3
    array[3]
    ptr + 3
    array +4
    array[4]
    ptr + 4
    ...
    array +24
    array[24]
    ptr + 24

    А обращение к пятому элементу массива можно записать как:

    array[4],    *( array + 4),      *(рtr + 4).

    Эти операторы вернут пятый элемент массива.

    Таким образом, С++ дает два способа обращения к элементу массива:

  • с помощью индексированного имени;
  • посредством арифметики с указателями.
  • Версия с индексированным именем выполняется медленнее, так как в языке С++ на индексирование массива уходит больше времени, чем на выполнение оператора *. Поэтому использование указателей для доступа к элементам массива в языке С++ применяется довольно часто.

    Пример 2.

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[]){
      int array[10]; 
      int *p;
      p=array[0];
      /*эквивалентные операции присваивания*/
      *array=2; printf("%d\n", array[0]);
      array[0]=2; printf("%d\n", array[0]);
      *(array+0)=2; printf("%d\n", array[0]);
      *p=2; printf("%d\n", array[0]);
      p[0]=2; printf("%d\n", array[0]);
      *(p+0)=2; printf("%d\n", array[0]);
      system("pause");
      return 0;
    }

    В результате выполнения любой из эквивалентных операций присваивания в данном примере начальному элементу массива присваивается значение 2, но быстрее всего компьютер выполнит операции *array=2 и *p=2, так как в них не требуется выполнять операции сложения.

    Использование элементов массивов в выражениях (операции с элементами массивов)

    С элементами объявленного массива можно выполнять все действия, допустимые для обычных переменных этого типа (выше был приведен пример целочисленного массива, т.е. типа int ). Например, возможны операторы присваивания:

    hours[4] = 34;
    hours[5] = hours[4]/2;

    или логические выражения с участием элементов массива:

    if (number < 4  hours[number] >= 40) { ... }

    Присвоить значения набору элементов массива часто бывает удобно с помощью циклов for или while. Для массивов не допустима операция прямого присваивания.

    Генерация одномерных массивов

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

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

    В большинстве задач с использованием массивов целесообразно выполнять генерацию массивов – автоматическое формирование значений элементов. Генерацию массива (массивов) в программе оформляют в виде отдельной функции.

    Стандартными способами генерация массивов являются:

  • ввод данных с клавиатуры,
  • формирование значений через генератор случайных чисел,
  • вычисление значений по формуле,
  • ввод данных из файла.
  • При этом при формировании значений элементов используют цикл по индексам элементов или арифметические операции с указателем на массив. В данной работе рассмотрим первые три способа генерации массивов ( Примеры 3, 4, 6 ).

    Вывод одномерных массивов

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

    Пример 3.

    /*Генерация целочисленного массива числами с клавиатуры и вывод массива в строку*/
    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    #define max 20
    
    void gen (int k,int *pp);//прототип функции генерации массива
    void out (int k,int x[max]);//прототип функции вывода массива
    
    int _tmain(int argc, _TCHAR* argv[]){
      int a[max],n,*p; 
      do {
        printf("\nВведите количество элементов массива n (n<=20):");
        scanf ("%d",n); 
        }
      while (n>max); //проверка выхода за границы массива
      p=a;
      gen(n,p);
      out(n,a);
      system("pause");
      return 0;
    }
    
    //Описание функции генерации массива с клавиатуры
    void gen(int k,int *pp){     
    /*передача указателя как параметра позволяет вернуть 
      сформированный массив в основную программу*/
      int i;
      printf("\nВведите значения %d элементов массива: \n",k); 
      for (i=0;i<k;i++){
        printf("x[%d]= ",i);
        scanf("%d",pp++);
      }
    }
    
    //Описание функции вывода массива в строку
    void out (int k,int x[max]){
      int i;
      printf("\nВывод значений %d элементов массива в строку: \n",k); 
      for (i=0;i<k;i++)
        printf("%d\t",x[i]);
    }

    Пример 4.

    /*Описание функции генерации массива значениями элементов арифметической прогрессии*/
    void gen(int k,int x[max]) {
      int i,d;
      printf ("\nВведите нулевой элемент прогрессии: ");
      scanf("%d",x[0]); 
      printf ("\nВведите разность прогрессии: ");
      scanf("%d",d);
      for (i=1;i<k;i++)
         x[i]=x[i-1]+d;
    }

    Пример 5.

    //Описание функции вывода массива в столбец
    void out (int k,int x[max]){
      int i;
      printf("\nВывод значений %d элементов массива в столбец: \n",k); 
      for (i=0;i<k;i++)
        printf("x[%i]= %d\n",i,x[i]);
    }

    Для использования функции генерации случайных чисел необходимо подключить библиотеку <time.h>.

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

  • Функция srand(). Синтаксис:
    void srand(unsigned seed);
    – функция устанавливает свой аргумент как основу ( seed ) для новой последовательности псевдослучайных целых чисел, возвращаемых функцией rand(). Сформированную последовательность можно воспроизвести. Для этого необходимо вызвать srand() с соответствующей величиной seed.

    Для использования данной функции необходимо подключить библиотечный файл <stdlib.h>.

  • Функция rand(). Синтаксис:
    int rand(void);
    – функция возвращает псевдослучайное число в диапазоне от нуля до RAND_MAX. Для использования данной функции необходимо подключить библиотечный файл <stdlib.h>.
  • Константа RAND_MAX определяет максимальное значение случайного числа, которое может быть возвращено функцией rand(). Значение RAND_MAX – это максимальное положительное целое число.
  • Часто в задачах требуется выполнить генерацию массива на произвольном промежутке [a,b). Для этого используются следующие выражения:
    //генерация случайных целых чисел на [a,b)
       x[i]=rand()%(b-a)+a;
    //генерация случайных вещественных чисел на [a,b)
       y[i]= rand()*1.0/(RAND_MAX)*(b-a)+a;
  • Пример 6.

    /*Описание функции генерации массива случайными вещественными числами на[a,b)*/
    void gen(int k,int a, int b, float x[max]){ 
      int i;
      srand(time(NULL)*1000); 
      //устанавливает начальную точку генерации случайных чисел
      for (i=0;i<k;i++){
       x[i]=(rand()*1.0/(RAND_MAX)*(b-a)+a); 
       //функция генерации случайных чисел на [a,b)
       }
    }

    Ключевые термины

    Генерация массива – это автоматическое формирование значений его элементов.

    Значение элемента массива – это значение, хранящееся по адресу, который соответствует данному элементу.

    Измерение массива – это количество индексов в определении массива.

    Имя массива – идентификатор, именующий выделенную под массив область памяти.

    Индекс элемента массива – это порядковый номер элемента в последовательности.

    Инициализация массива – это формирование значений его элементов.

    Массив – это именованная последовательность областей памяти, хранящих однотипные элементы.

    Одномерный массив – это массив, измерение которого равно единице.

    Размер массива – это количество элементов в массиве.

    Тип массива – это тип элементов массива.

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

    Элемент массива – это каждая область памяти из последовательности областей, выделенных под массив.

    Краткие итоги

  • Массив является представителем структурированного типа данных в языке С++.
  • Элементы массива имеют одинаковые имя, тип и располагаются в памяти последовательно.
  • Элементы массива характеризуются индексами, значениями и адресуемой памятью.
  • Существует две основные формы объявления массивов: с указанием и без указания размера. Безразмерный массив объявляется, если: он инициализируется при объявлении, является формальным параметром функции, объявлен как ссылка на массив.
  • Инициализация массива заключается в присваивании начальных значений его элементам. В программах часто используется генерация массивов.
  • Для массивов нельзя выполнить операцию прямого присваивания.
  • Адресация элементов массива осуществляется с помощью индексированного имени. Обращаться к элементам массива можно также посредством механизма указателей.
  • Размер памяти, занимаемой массивом, зависит от реализации и вычисляется с помощью операции sizeof.
  • Массивы используются для решения прикладных задач.
  • Лабораторная работа 10. Массивы: одномерные массивы

    Цель работы: изучить понятия, особенности внутреннего представления, способов генерации и вывода одномерных массивов, научиться выполнять объявление, инициализацию, генерацию и вывод массивов при решении задач на языке C++.

    При выполнении лабораторной работы для каждого задания требуется написать программу на языке С++, которая получает на входе числовые данные (в зависимости от постановки задачи), выполняет генерацию, вывод одномерного массива указанного типа. Ввод данных осуществляется с клавиатуры с учетом требований к входным данным, содержащихся в постановке задачи. Ограничениями на входные данные является диапазон используемого числового типа данных в языке С++ и максимально допустимый размер объявляемого одномерного массива.

    Теоретические сведения.

    Ознакомьтесь с материалом лекции 10.

    Задания к лабораторной работе.

    Выполните приведенные ниже задания.

  • Объявите одномерный вещественный массив, в котором 10 элементов. Выполните генерацию массива, используя закономерность: 0; 0,1; 0,12; 0,123,… Выведите массив на экран в столбик. Оформите генерацию и ввод массива с помощью функций.
  • Объявите одномерный целочисленный массив, в котором 15 элементов. Выполните генерацию массива первыми 15 числами Фибоначчи. Выведите массив на экран в строку. Оформите генерацию и ввод массива с помощью функций.
  • Даны два натуральных числа a и b ( a<b ). Объявите одномерный целочисленный массив, в котором 10 элементов. Выполните генерацию массива первыми десятью цифрами дробной части частного a/b. Выведите массив на экран в столбец. Оформите генерацию и ввод массива с помощью функций. Например, для a =7, b =23 значениями элементов массива будут числа: 3 0 4 3 4 7 8 2 6 0.
  • Объявите одномерный целочисленный массив, в котором не более 100 элементов. Выполните генерацию массива первыми 100 простыми числами. Выведите массив на экран в строку (или в строки по 10 элементов в каждой). Оформите генерацию и ввод массива с помощью функций.
  • Указания к выполнению работы.

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

    Следует реализовать каждое задание в соответствии с приведенными этапами:

  • изучить словесную постановку задачи, выделив при этом все виды данных;
  • сформулировать математическую постановку задачи;
  • выбрать метод решения задачи, если это необходимо;
  • разработать графическую схему алгоритма;
  • записать разработанный алгоритм на языке С++;
  • разработать контрольный тест к программе;
  • отладить программу;
  • представить отчет по работе.
  • Требования к отчету.

    Отчет по лабораторной работе должен соответствовать следующей структуре.

  • Титульный лист.
  • Словесная постановка задачи. В этом подразделе проводится полное описание задачи. Описывается суть задачи, анализ входящих в нее физических величин, область их допустимых значений, единицы их измерения, возможные ограничения, анализ условий при которых задача имеет решение (не имеет решения), анализ ожидаемых результатов.
  • Математическая модель. В этом подразделе вводятся математические описания физических величин и математическое описание их взаимодействий. Цель подраздела – представить решаемую задачу в математической формулировке.
  • Алгоритм решения задачи. В подразделе описывается разработка структуры алгоритма, обосновывается абстракция данных, задача разбивается на подзадачи. Схема алгоритма выполняется по ЕСПД (ГОСТ 19.003-80 и ГОСТ 19.002-80).
  • Листинг программы. Подраздел должен содержать текст программы на языке программирования С++, реализованный в среде MS Visual Studio 2010.
  • Контрольный тест. Подраздел содержит наборы исходных данных и полученные в ходе выполнения программы результаты.
  • Выводы по лабораторной работе.
  • Ответы на контрольные вопросы.
  • Контрольные вопросы

  • Почему в программе на С++ необходимо, чтобы был известен размер массива?
  • Можно ли выполнить прямое присваивание массивов объявленных так: int x[10], y[10];?
  • Когда, с какой целью и почему возможно объявление безразмерных массивов?
  • В чем отличие обращения к элементам массива с помощью индексированного имени и посредством арифметики с указателями?
  • Может ли значение элемента массива использоваться в качестве индекса другого элемента массива?
  • Эквивалентны ли для массива mas следующие обращения и почему: mas и mas[0]?
  • Какие ограничения распространяются на тип массива?
  • Каким образом можно определить объем памяти, выделяемой под массив?
  • Каким образом можно составить выражение для генерации массива случайными целыми числами на заданном промежутке?
  • Вернуться к учебному плану