Сделай Сам Свою Работу на 5

Понятие массива. Описание массива.





Массив в языке Си - это непрерывный набор однотипных переменных, имеющих одинаковое имя. Элемент массива обозначается его номером (индексом), указанным в [ ] после имени. Нумерация индексов начинается с 0.

Например, a[0] обозначает 0-й элемент массива a, mas_line[5] - пятый элемент массива mas_line, а d[i] - элемент массива d с номером, который равен значению переменной i. При i=3 это третий элемент, а при i=1 -первый. Преимущество использования массивов как раз и заключается в том, что к разным переменным можно обращаться одной и той же записью типа d[i], меняя только значение переменной, обозначающей индекс.

До своего использования массив должен быть описан. Описание имеет вид (синтаксис):

тип имя[константа];

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

int a[10]; char my_string[80];

Семантика: описание нужно транслятору, чтобы отвести в памяти непрерывный участок для всех элементов массива. Длина этого участка вычисляется произведением размера типа данных на количество элементов массива. Например, размер памяти, отведенный под массив a, равен 2 байта * 10 элементов - всего 20 байт, а под массив my_string - 1*80=80 байт.



При обращении к элементу массива в процессе выполнения программы выход значения индекса за его максимальное значение, указанное при описании массива, не проверяется. Поэтому необходимо следить за этим самостоятельно, чтобы избежать трудно выявляемых ошибок. Индекс может изменяться от 0 до значения (константа - 1).

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

        - 0-й элемент исходного массива
        - 1-й элемент исходного массива
        - 2-й элемент исходного массива

Поэтому при описании многомерных массивов количество элементов по каждому новому измерению дописывается справа от предыдущего измерения в [ ]:

тип имя[константа1] [константа2]...[константаN];



Например, если вышеуказанная матрица из 3-х строк и 4-х столбцов имеет вещественные элементы, то ее описание может выглядеть так:

float matr[3][4];

matr[0][1]
На первом месте всегда указывается количество строк, на втором - столбцов. В одномерной памяти ПК транслятор располагает двумерный массив построчно - сначала элементы первой строки, затем второй и т.д.:

4 байта                    
                       
Строка 1 Строка 2 Строка 3
                       

 

Каждый элемент такого массива определяется его двумя индексами, например, matr[0][1] означает второй элемент первой строки матрицы (напомним, что нумерация начинается с 0, поэтому 0 - первая строка, а 1 - второй столбец).

Количество индексов элемента массива определяет его размерность, максимальные значения индексов - его размер. Например, указанный массив имеет размерность 2, а размер - (3´4).

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

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

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

Инициализация одномерного массива при объявлении выглядит так:

тип имя[константа] = {список инициализаторов};

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

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

Если количество инициализаторов меньше размера массива, оставшиеся элементы обнуляются. Например, объявление



float b[3]={2.}; заполнит массив b числами 2.,0.,0.

Если есть список инициализаторов, то количество элементов массива при объявлении можно не указывать, транслятор сам отведет ему место в соответствии со списком инициализаторов. Например, объявление int c[]={2,0,5}; создаст массив c из трех чисел.

При инициализации двумерного массива значения его элементов нужно указывать в порядке их расположения в памяти, т.е. по строкам. Например, объявление int d[3][2]={1,3,5,7}; создаст матрицу

, которая в памяти выглядит так:
     
                 

Если нужно изменить порядок заполнения (т.е. указывать не все элементы строк), то каждую строку нужно указывать в { }.Например, объявление int d[3][2]={{1},{3,5},{7}}; создаст матрицу

, которая в памяти выглядит так:
     
                 

Размер для первого индекса (т.е. количество строк) можно не указывать, транслятор сам определит его в соответствии со списком инициализаторов. Например, объявление int d[][2]={1,3,5,7}; создаст массив (2´2).

Инициализация массивов вводом с клавиатуры.

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

1) Ввод одномерного массива из n элементов через пробел:

cout << "\nВведите " << n << " чисел через пробел\n";

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

cin >> a[i];

2) Ввод двумерного массива размером (n´m) построчно:

printf("\nВведите матрицу (%d´%d) построчно\n",n,m);

for (i=0; i<n; i++)// Для каждой строки

for (j=0; j<m; j++)// ввод m чисел через пробел

cin >> d[i][j];

Вывод массивов.

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

1) Вывод одномерного массива из n элементов через пробел:

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

cout >> a[i] >> " ";

2) Вывод двумерного массива размером (n´m) построчно:

for (i=0; i<n; i++)// Для каждой строки

{ for (j=0; j<m; j++)// вывод m чисел через 2 пробела

printf("%5.1f ",a[i][j]); //число в 5 позициях

cout << '\n';// переход на след.строку экрана

}

Работа с массивами.

Приведем в качестве примера работы с массивами фрагменты программы нахождения суммы и максимального элемента массива a из n чисел (блок-схемы соответствующих алгоритмов были приведены в 1.2.4.

#define MAX_SIZE 100 //Определение именованной константы

float a[MAX_SIZE];

float s,max;

int i,n;

············ // Ввод n и массива a из n чисел, n должно быть ············ // не больше MAX_SIZE

s=0; // Сумма элементов массива a

max=a[1]; // Максимальный элемент массива

for (i=1;i<n;i++) // Для каждого элемента

{ s+=a[i]; // Увеличение суммы на a[i]

if (a[i]>max) // Если элемент больше максимального, то

max=a[i]; // он становится максимальным

}

············ // Вывод n, массива a, s и max

Указатели.

Адреса и указатели.

Напомним, что память ЭВМ представляет собой последовательность пронумерованных ячеек, каждая из которых имеет свой адрес. Там хранятся как команды программы, так и данные, ею используемые. Данные типа char в языке Си занимают один байт, данные целого типа - обычно два байта, а вещественного - 4 байта.

Если рассмотреть операцию a=5, то для компилятора это означает: занести число 5 по адресу переменной с именем a, т.е. в ячейку с адресом 2008 (см.таблицу).

Имя ptr   a d
Адрес
Значение  

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

тип_объекта *имя_указателя ;

Это означает, что в этом указателе будет храниться адрес какого-либо объекта (например, переменной) указанного типа. Например,

int *ptr, d;

означает, что в указателе ptr будет храниться адрес переменной целого типа, а в переменной d - значение целого типа.

Занести в указатель адрес можно, используя специальную операцию адресации: &имя_переменной , например, ptr=&d означает: занести в указатель ptr адрес переменной d. Говорят, что ptr указывает (или ссылается) на d.

Чтобы изменить значение переменной, используя ее указатель (т.е. значение по указанному адресу), нужно вместо имени этой переменной в левой части оператора присваивания использовать имя указателя на эту переменную со знаком * перед ним. Например, операторы *ptr=32; и d=32; равнозначны, если ptr ссылается на d.

Операцию * называют операцией раскрытия ссылки, она означает переменную, на которую ссылается указатель. Выражение *указа­тель может использоваться вместо имени соответствующей переменной в любых выражениях Си (в нашем случае *ptr вместо d). Унарные операции & и * имеют тот же приоритет, что и унарный минус (см. таблицу в 2.4.3.5).

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

Пусть описаны целые массив a, переменная s и указатель на целое p.

int *p,a[5]={7,-3,8,-2,4},s;

В языке Си имя массива (например, a) означает на самом деле адрес его нулевого элемента, т.е. адрес ячейки a[0]. Поэтому операторы

p=&a[0]; и p=a;

эквивалентны и предписывают занести в p адрес нулевого элемента массива a (т.е. 2004). Оператор s=*p; копирует в s содержимое a[0].

Имя p a[0] a[1] a[2] a[3] a[4] s
Адрес
Значение -3 -2

Если p указывает на нулевой элемент массива a, то по определению p+1 указывает на следующий элемент, т.е. на a[1], а значением выражения *(p+1) является значение этого элемента, т.е. -3. Аналогично p+i (также, как и a+i) указывает на i -й элемент массива a, следовательно значения выражений a[i] , p[i], *(a+i) и *(p+i) равны.

Отличием в использовании имени массива и указателя на массив является то, что указатель - это переменная и в нее можно занести в дальнейшем какой-либо другой адрес, например, записать p++ (тогда в p будет находиться адрес 2006. С именем массива такое сделать нельзя, т.к. адрес массива определен компилятором раз и навсегда.

Строки и указатели.

Наибольшее распространение указатели получили в Си при работе со строками. Строковые константы, т.е. объекты вида "текст" (см.2.3), представляются в Си в виде массива символов, который заканчивается символом '\0'. Количество элементов этого массива на единицу больше количества символов между двойными кавычками. Например, "Си" представляется массивом из 3 символов 'С', 'и' и '\0'.

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

char *p_error;

p_error="Ошибка!";

заносят в указатель p_error адрес нулевого элемента символьного массива (символа 'О'). Это же можно сделать и так:

char *p_error=" Ошибка!";

Описание же следующего вида

char m_error[]="Ошибка!"; создает символьный массив m_error из 8 элементов. Отдельные его символы можно будет изменить, но m_error всегда ссылается на одно и то же место памяти Значение же p_error в будущем можно будет изменить, записав туда адрес другой константы, например, p_error="Ошибочные данные!";.

Для ввода строки с клавиатуры предусмотрена функция gets(имя_массива), которая заносит введенные с клавиатуры символы в указанный символьный массив. Размер массива должен быть таким, чтобы вместить все введенные символы. Если же в качестве аргумента функции указан указатель на char, то он также должен указывать на область памяти, достаточную, чтобы вместить все введенные символы.

При вводе строки после нажатия пользователем клавиши Enter символы заносятся в массив, за последним введенным символом автоматически добавляется символ ‘\0’. Затем выполнение программы продолжается.

Чаще всего строки используются в качестве аргументов функций, как, например, в функции fprintf() или в потоках вывода cout.

Рассмотрим некоторые приемы работы со строками в Си. Основным является то, что длина строки заранее неизвестна. Признаком ее окончания является символ '\0'.

Пример 19. Напишем программу, которая позволяет ввести с клавиатуры пароль и сравнить его с образцом.

int i,equal=0; // Признак совпадения строк пока равен 0

unsigned char mas[81], *parol="Мой пароль";

 

gets(mas); // Ввели пароль (<=80 символов)

for (i=0; mas[i] == *(parol+i);i++)

if (mas[i] == '\0' ) // Строки совпали полностью

{equal=1;break;} // Признак совпадения = 1

if (!equal)

printf("Пароль %s не годится\n",mas);

else

cout << "Пароль" << mas << "годится\n";

На вводимое значение пароля мы отвели 80 символов, указатель parol ссылается на массив с его верным значением. После ввода пароля он посимвольно сравнивается с правильным значением до нахождения отличающихся символов. Если же дошли до конца строки (символа ‘\0’), не встретив отличий, то выходим из цикла, установив признак совпадения строк в 1.

Пример 20. Напишем программу, которая позволяет определить количество букв и слов в введенном предложении.

Ограничим длину предложения 300 символами, признаком окончания слова будем считать пробел, а признаком буквы – значение кода символа в пределах от 65 (латинская А) до 239 (русская я). Поскольку будем использовать коды кириллицы (т.е. > 127), то необходимо описывать символы как unsigned char.

int i=0; // Номер текущего символа

int k_sl=1,k_sim=0; // Количество слов и символов

unsigned char mas[300],ch; // Предложение и текущий символ

gets(mas);

while ( (ch = *(mas+i++)) != '\0' )

if (ch==' ') // Текущий символ - пробел -

k_sl++; // увеличили кол-во слов

else if (ch>64 && ch<240) // Текущий символ - буква -

k_sim++; // увеличили кол-во букв

printf("Слов - %d, ,букв - %d",k_sl,k_sim);

Просматриваем по порядку все введенные символы, записывая их в ch, пока не дойдем до конца строки '\0'. Выражение (ch = *(mas+i++)) != '\0' означает: взять то, что находится по адресу mas+i, и занести в ch. Затем сравнить это значение с '\0' и результат сравнения считать значением всего выражения. Затем увеличить i на 1.

Внешние скобки здесь обязательны, иначе в ch занесется резуль­тат сравнения *(mas+i) и '\0', поскольку логическая операция != обла­дает более высоким приоритетом, чем операция присваивания =.

Отметим, что в операторе while вместо mas+i++ использовать сдвиг значения mas путем mas++ нельзя, т.к. mas - имя массива, а не указатель. Можно сделать указатель на mas:

insigned *p;

p=mas; //и тогда правомерна конструкция

while ( (ch = *(p++)) != '\0' )

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.