Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Одномерные массивы представляют собой список взаимосвязанных переменных. Такие списки широко используются в программировании. Для современного программирования массивы являются фундаментальными



Одномерные массивы

Одномерные массивы представляют собой список взаимосвязанных переменных. Такие списки широко используются в программировании. Для современного программирования массивы являются фундаментальными объектами.

Общая форма одномерного массива выглядит так: тип имя [ размер ]; Обращение к индивидуальным элементам массива осуществляется с помощью индексов. Индекс определяет позицию элемента внутри массива. В С++ все массивы используют ноль в качестве индекса своего первого элемента!

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

int my_array [3];

my_array[0] = 12;

my_array[1] = 1245;

my_array[2] = 18094;

std::cout << std::setw(6) << my_array[0] << std::setw(6) << my_array[1]

<< std::setw(6) << my_array[2] << std::endl;

getch ();

return 0;

}

 

 

 

 

   

 

 

       

 

         

 

или

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int n = 3;

int my_array [n];

my_array[0] = 12;

my_array[1] = 1245;

my_array[2] = 18094;

std::cout << std::setw(6) << my_array[0] << std::setw(6) << my_array[1]

<< std::setw(6) << my_array[2] << std::endl;

getch ();

return 0;

}

Обратите внимание на присутствие спецификатора const. При объявлении массива его размерность должна быть определена целым положительным числом (как в первом случае) или целой положительной константой. Объявление int my_array [ n ] без предварительной инициализации переменной n и объявлении ее константой вызовет ошибку компиляции. Запрещено также объявление массива без указания размерности: int my_array [] за исключением объявления с инициализацией (см. ниже инициализация массивов). Это происходит от того, что память под все элементы массива в стеке выделяется на этапе компиляции. Исключение составляет объявление массива в динамической памяти (см. ниже). Далее память, используемая для хранения массива, должна оставаться выделенной на все время существования массива.

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

С++ допускает возможность создания многомерных массивов. Простейшей формой такого массива является двумерный массив. Для того, чтобы создать двумерный массив, надо записать: тип имя [ число строк ][ число столбцов ]; Обратите внимание, что левый индекс нумерует строки, а правый столбцы. В отличие о других языков программирования, которые для разделения размерностей массива используют запятые, в С++ каждый размер берется в квадратные скобки.

int my_array [3][4];

или

const int n = 3;



const int m = 4;

int my_array [n][m];

Двумерные массивы хранятся в памяти в виде матрицы, состоящей из строк и столбцов. Доступ к элементам массива осуществляется по двум индексам. Двумерный массив можно рассматривать как одномерный массив, содержащий, в свою очередь, в качестве элементов одномерные массивы! При этом число строк является размерностью самого массива, а число столбцов – размерностью массивов его составляющих. Т.е. массив int my_array [3][4] – это трехмерный массив, состоящий из массивов по четыре элемента в каждом:

my_array [0][0]

my_array [0][1]

my_array [0][2]

my_array [0][3]

my_array [1][0]

my_array [1][1]

my_array [1][2]

my_array [1][3]

my_array [2][0]

my_array [2][1]

my_array [2][2]

my_array [2][3]

 

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

В С++ предусмотрено несколько способов инициализации массивов:

1. Инициализация при объявлении

a) int my_array [3] = {12, 1245, 18094}; // Важно!! Следующий фрагмент кода не является правильным!!

int our_array [3];

our_array [3]= {12, 1245, 18094}; Ошибка компиляции!! Так инициализировать можно только при объявлении. Иначе см. пункт 2.

b) const int n = 3; int my_array [n] = {12, 1245, 18094};

c) int my_array [ ] = {12, 1245, 18094}; // при этом компилятор сам определяет размерность массива в процессе компиляции.

d) int my_array [5] = {12, 1245, 18094}; // в данном случае компилятор инициализирует нулями два оставшихся элемента массива.

2. Инициализация в цикле

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int n = 5;

int my_array [n];

for (int i = 0; i < n; i++)

{

std:: cout << "Enter" << i << "element of the my_array"

<< std::endl;

std::cin >> my_array[i];

}

for (int i = 0; i < n; i++)

{

std::cout << std::setw(6) << my_array[i];

}

getch ();

return 0;

}

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

int our_array [5];

our_array = my_array; Ошибка компиляции!!

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

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int n = 5;

int my_array [n]={12, 1245, 18094, 245, 123};

int our_array[n];

for (int i = 0; i < n; i++)

{

our_array[i] = my_array[i];

}

for (int i = 0; i < n; i++)

{

std::cout << std::setw(6) << our_array[i];

}

getch ();

return 0;

}

 

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

Многомерные массивы инициализируются так же, как и одномерные.

1. Инициализация при объявлении

a) int my_array [4][3] = { {12, 1245, 18094}, {2, 518, 45}, {45, 67, 45}, {34, 562, 98} }; // если опустить внутренние фигурные скобки, то результат не изменится. В любом случае, сначала будут заполняться строки, а затем столбцы.

b) const int n = 4;

const int m=3;

int my_array [n][m] = { {12, 1245, 18094}, {2, 518, 45}, {45, 67, 45}, {34, 562, 98} };

c) int my_array [ ][3] = { {12, 1245, 18094}, {2, 518, 56}, {45, 67, 45}, {34, 562, 98} }; // можно не указывать размерность основного массива. При этом размерности вложенных массивов должны быть указаны. int my_array [ ][ ] = { {12, 1245, 18094}, {2, 518, 56}, {45, 67, 45}, {34, 562, 98} }; Ошибка компиляции!!

d) int my_array [4][3] = { {12, 1245, 18094}, {2, 518}, {45, 67, 45}, {34, 98} }; // Можно пропустить инициализацию значений в любой строке, при этом значение автоматически будет инициализировано нулем.

2. Инициализация в цикле

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int row = 3;

const int column = 4;

int data [row][column];

for (int i = 0; i < row; i++)

{

for (int j = 0; j < column; j++)

{

std::cin >> data [i][j];

}

}

return 0;

}

Обратите внимание на использование вложенных циклов for при инициализации многомерных массивов. Массив инициализируется построчно.

Задание: самостоятельно записать вывод массива на экран.

 

Массивы и указатели

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

Рассмотрим следующий код.

#include<iostream>

#include<conio.h>

int main(int argc, char* argv[])

{

const int row = 5;

int data [row]={51, 4, 67, 46, 98};

int *pData = &data[0];

std::cout << pData << std::endl;

std::cout << data << std::endl;

getch();

return 0;

}

По сути, массив data[row] – это блок из пяти последовательных элементов с именами data[0], data[1], data[2], data[3] и data[4]. Запись data[i] означает i-ый элемент массива.

Тогда указатель pData содержит адрес первого элемента массива, т.е. указывает на первый элемент data[0]. Соответственно по определению указателей, указатель pData+1 указывает на второй элемент массива data[1], pData+i на i-ый элемент массива. Соответственно, если pData+i адрес элемента data[i], то *(pData+i) сам элемент data[i]. Между обращением к массиву и адресной арифметикой существует близкое родство. По определению имя массива – это одновременно адрес его нулевого элемента. Поэтому, следующие операции присваивания идентичны:

int *pData = &data[0];

int *pData = data;

Однако, есть и одно различие между именем массива и указателем, которое следует иметь ввиду. Указатель является переменной, так что выражения pData = data и pData++ вполне допустимы. А вот конструкции наподобие data=pData и data++ не разрешены!!

Указателю можно присвоить адрес любого элемента массива:

#include<iostream>

#include<conio.h>

int main(int argc, char* argv[])

{

const int row = 5;

int data [row]={51, 4, 67, 46, 98};

int *pData_1 = &data[2];

std::cout << pData_1 << std::endl;

std::cout << data << std::endl;

std::cout << pData_1 – data << std::endl;

getch();

return 0;

}

При этом (pData_1+i) указывает на элементы, стоящие после data[2], а (pData_1−i) на элементы, стоящие перед data[2]. Если к двум указателям на объекты одного и того же типа (в данном случае указателям на элементы массива) применить операцию вычитания, то в результате получится целочисленное значение со знаком, представляющее расстояние между указываемыми объектами; так указатели pData_1 и data различаются на два.

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

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int n = 5;

int my_array [n];

for (int *my_ptr = my_array; my_ptr - my_array < n; my_ptr++)

{

std:: cout << "Enter" << my_ptr - my_array << "element of the my_array"

<< std::endl;

std::cin >> *my_ptr;

}

for (int i = 0; i < n; i++)

{

std::cout << std::setw(6) <<*(my_array+i);

}

getch ();

return 0;

}

 

Массивы, расположенные в динамической памяти.

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

const int n = 5;

int my_array [n];

зарезервирована память для пяти элементов типа int. Массивы являются эффективным подходом к хранению данных, но они имеют серьезный недостаток: мы должны знать при написании программы, насколько большой массив нам нужен. Следующий код работать не будет!!

std::cin >> n;

int my_array [n]; // Ошибка компиляции!! Размер массива должен быть константой!

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

1. Определить размерность массива достаточно большим числом, но это приводит к перерасходу памяти.

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

3. Использование стандартной библиотеки шаблонов STL. При этом различного рода контейнеры являются динамически расширяемыми массивами.

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

double *parr = 0;

pa = new double[10];

или

double *parr = new double[10];

При этом в динамической памяти выделяется блок, состоящий из десяти элементов типа double, а указатель parr содержит адрес нулевого элемента массива. Обращение к элементам массива происходит либо посредством разыменовывания указателя, т.е *(parr+ i) является i -м элементом массива, либо посредством индексации массива: parr[ i ] также представляет собой значение i -го элемента массива.

Отметим два главных отличия массива расположенного в динамической памяти, и массива, расположенного в стеке:

1. Массив, расположенный в стеке может быть инициализирован при объявлении (см. выше), а массив, определенный в динамической памяти, может быть инициализирован только поэлементно.

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

std::cin >> n;

int *my_array=new int [n];

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

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

delete [ ] my_array;

Обратите внимание на квадратные скобки после оператора delete. Если их опустить, то произойдет удаление только нулевого элемента массива!!! Память, занимаемая остальными элементами, освобождена не будет. Квадратные скобки сообщают компилятору, что удаляется массив. Компилятор вычисляет размер всех элементов массива и освобождает занимаемую им область динамической памяти. Напомним, что после удаления массива указатель по-прежнему указывает на область памяти, где раньше находился массив. Поэтому указатель надо обнулить, если нет необходимости его использования в дальнейшем, или присвоить ему другой адрес.

 

Массивы указателей и указатели на указатели

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

const int n = 5;

double * ptr_array_stack [n];

и для массива, размещенного в динамической памяти:

std::cin >> n;

double **ptr_array_heap=new double* [n];

Обратите внимание на две звездочки в выражении слева. В данном случае double* означает тип переменных, содержащихся в массиве (этот тип также указывается в выражении справа), а *ptr_array_heap указатель на ячейку памяти, содержащую нулевой элемент массива. Иными словами, ptr_array_heap является указателем на массив указателей, т.е. двойным указателем. Поскольку, как говорилось выше, имя массива является указателем на его первый элемент, то в данном случае имя массива имеет тип указатель на указатель:

double **parray = & ptr_array_stack[0];

или

double **parray = ptr_array_stack;

double **parray_h = ptr_array_heap + 1;

 

Таким образом, в массив указателей можно помещать адреса объектов. Заметим, что массив указателей может также быть инициализирован пользователем. Однако для этого следует использовать функцию reinterpret_cast< тип >(приводимое выражение), которая осуществляет привидение типа целых чисел в тип указателей:

 

int main()

{

const int n = 5;

double ** my_array = new double* [n];

for (double ** my_ptr = my_array; my_ptr - my_array < n; my_ptr++)

{

int temp;

std:: cout << "Enter " << my_ptr - my_array << " element of the my_array"

<< std::endl;

std::cin >> temp;

*my_ptr= reinterpret_cast<double*>(temp);

}

for (int i = 0; i < n; i++)

{

std::cout << std::setw(12) <<*(my_array+i);

}

std::cout << std::endl;

delete [ ] my_array;

my_array=0;

 

return 0;

}

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

 

Многомерные массивы и указатели.

Двумерные массивы можно рассматривать как частный случай одномерного массива указателей. Рассмотрим два объявления-определения:

const int n = 3; const int m = 4;

double arr_1 [n][m];

double *arr_2[n];

Как arr_1[i][j] или *(*(arr_1+i)+j), так и arr_2[i][j] или *(*(arr_2+i)+j) – вполне законные обращения к одному элементу данных типа int. При этом arr_1 – полноправный двумерный массив; для него выделяется память в размере n×m ячеек по восемь байт, и для поиска элемента используется перевод координат прямоугольного массива в линейный адрес по формуле i×m+j. А для переменной arr_2 выделяется n ячеек по четыре байта. Если предположить, что каждый элемент arr_2 указывает на массив из m элементов, то получим те же самые n×m ячеек типа double плюс n ячеек на указатели. Важное преимущество массива указателей состоит в том, что строки массива могут иметь различную длину, т.е. каждый элемент arr_2 не обязан указывать на массив из десяти элементов. Одни могут указывать на два элемента, другие – на один, а третьи – вообще ни на что. Как было отмечено выше, имя массива является указателем на нулевой элемент. В случае массива указателей элементом является сам указатель, а двумерного массива – строка, т.е. массив.

Таким образом, имя двумерного массива или массива указателей имеет тип указателя на указатель, т.е. двойного указателя. Следовательно, обращения к элементам двумерного массива осуществляется посредством двойного разыменовывания указателя*(*(arr_1+i)+j). Это эквивалентно

arr_1[i][j];

*(arr_1[i]+j);

(*(arr_1+i))[j];

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

double ** pp = arr_1; // Ошибка компиляции!!

double ** pp = arr_2; // Правильно!

 

 

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

int n = 5;

double ** my_array = new double* [n];

 

Примеры:

 

 

#include<iostream>

#include<conio.h>

int main(int argc, char* argv[])

{

const int row = 3;

int column = 4;

int *data [row];

for (int i=0; i<row; i++)

{

*(data+i)=new int[column];

for (int j=0; j<column; j++)

{

std::cout << "Enter " << i << " " << j

<< " elements of data:" << std::endl;

std::cin >> *(*(data+i)+j);

}

}

for (int i=0; i<row; i++)

{

for (int j=0; j<column; j++)

{

std::cout << std::setw(3) << *(*(data+i)+j);

}

std::cout << std::endl;

}

int *pData_1=&(**data);

int *pData_2=*data;

int **pData_3=data;

std::cout << data << std::endl;

std::cout << pData_1 << std::endl;

std::cout << pData_2 << std::endl;

std::cout << pData_3 << std::endl;

for (int i=0; i<row; i++)

{

delete [ ]*(data+i);

}

pData_3=0;

pData_2=0;

pData_1=0;

_getch();

return 0;

}

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

int row = 3;

int column = 4;

int **data = new int*[row];

int *pData_1=&(**data);

int *pData_2=*data;

int **pData_3=data;

for (int i=0; i<row; i++)

{

*(data+i)=new int[column];

for (int j=0; j<column; j++)

{

std::cout << "Enter " << i << " " << j

<< " elements of data:" << std::endl;

std::cin >> *(*(pData_3+i)+j);

}

}

for (int i=0; i<row; i++)

{

for (int j=0; j<column; j++)

{

std::cout << std::setw(3) << *(*(data+i)+j);

}

std::cout << std::endl;

}

std::cout << data << std::endl;

std::cout << pData_1 << std::endl;

std::cout << pData_2 << std::endl;

std::cout << pData_3 << std::endl;

for (int i=0; i<row; i++)

{

delete [ ]*(data+i);

}

delete [ ] data;

data=0;

pData_3=0;

pData_2=0;

pData_1=0;

_getch();

return 0;

}

 

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

 

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

const int row = 3;

int column=4;

int *data [row];

for (int **i=data; i-data<row; i++)

{

*i=new int[column];

for (int *j=*i; j-(*i)<column; j++)

{

std::cout << "Enter " << i-data << " " << j-(*i)

<< " elements of data:" << std::endl;

std::cin >> *j;

}

}

for (int i=0; i<row; i++)

{

for (int j=0; j<column; j++)

{

std::cout << std::setw(3) << *(*(data+i)+j);

}

std::cout << std::endl;

}

int *pData_1=&(**data);

int *pData_2=*data;

int **pData_3=data;

std::cout << data << std::endl;

std::cout << pData_1 << std::endl;

std::cout << pData_2 << std::endl;

std::cout << pData_3 << std::endl;

for (int i=0; i<row; i++)

{

delete [ ]*(data+i);

}

pData_3=0;

pData_2=0;

pData_1=0;

_getch();

 

return 0;

}

#include<iostream>

#include<iomanip>

#include<conio.h>

int main(int argc, char* argv[])

{

int row = 3;

int column = 4;

int **data = new int*[row];

int *pData_1=&(**data);

int *pData_2=*data;

int **pData_3=data;

for (int **i=data; i-data<row; i++)

{

*i=new int[column];

for (int *j=*i; j-(*i)<column; j++)

{

std::cout << "Enter " << i-data << " " << j-(*i)

<< " elements of data:" << std::endl;

std::cin >> *j;

}

}

for (int i=0; i<row; i++)

{

for (int j=0; j<column; j++)

{

std::cout << std::setw(3) << *(*(data+i)+j);

}

std::cout << std::endl;

}

std::cout << data << std::endl;

std::cout << pData_1 << std::endl;

std::cout << pData_2 << std::endl;

std::cout << pData_3 << std::endl;

for (int i=0; i<row; i++)

{

delete [ ]*(data+i);

}

delete [ ] data;

data=0;

pData_3=0;

pData_2=0;

pData_1=0;

_getch();

return 0;

}

 

Обратите внимание на то, что указатели pData_1, pData_2 и pData_3 в первом случае, когда массив указателей определяется в стеке, а массивы, на которые эти указатели указывают в динамической памяти инициализируются после ввода массива. Во втором случае, когда и массив указателей, и сами массивы определены в динамической памяти, указатели pData_1, pData_2 и pData_3 можно инициализировать перед вводом массива.

 

 


Дата добавления: 2015-09-29; просмотров: 29 | Нарушение авторских прав




<== предыдущая лекция | следующая лекция ==>
Die Ewigkeit: Assiah. Сила Эсперов, основанная на измененном восприятии действительности, персональной реальности, позволяющая использовать законы квантового мира | 8. To type keys on a computer

mybiblioteka.su - 2015-2024 год. (0.079 сек.)