Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
консплекС++1к 2013.doc
Скачиваний:
0
Добавлен:
31.12.2019
Размер:
667.14 Кб
Скачать

Лекція 6. Масиви

План лекції (4 години)

  1. Оголошення масиву та ініціювання масиву

  2. Доступ до елементів масиву

  3. Масиви символів

  4. Багатомірні масиви

Масив можна уявити як змінну, що містить впорядкований набір даних одного типу. До кожного елементу масиву можна отримати доступ за його адресою. У мовах С/С++ масив не є стандартним типом даних, а може мати один з базових типів:char, int, float, double, тощо. Допускається створення масивів вказівників, структур та інші. Принципи побудови масивів та роботи з ними в основі однакові у С та С++..

Нижче названі чотири основних принципи, які визначають властивості масивів: у масиві зберігаються окремі значення, які називаються елементами:

  • всі елементи масиву повинні мати однаковий тип;

  • всі елементи масиву зберігаються в пам’яті послідовно, і перший елемент має нульове зміщення адреси, тобто нульовий індекс;

  • ім’я масиву є константою та містить адресу першого елементу масиву.

Оскільки всі елементи масиву мають однаковий, наперед встановлений розмір, а ім’я масиву містить адресу першого його елементу, тому неважко обчислити адресу довільного іншого елементу. Але при цьому потрібно дотримуватися такого принципу – суворої послідовності зберігання в пам’яті всіх елементів масиву, від нульового до останнього, при цьому перший елемент має найменшу адресу, а останній – найбільшу.

Ім’я масиву є константним виразом, який не змінюється протягом виконання програми, тому воно не може бути лівостороннім значенням. Якби не було такого обмеження, тоді програма могла б змінювати вміст імені, а суть такої зміни полягає у заміні нульового елементу масиву. Не дивлячись на те, що таке ствердження може показатись незначним, проте насправді деякі вирази, які мають вигляд цілком коректними, виявляються недопустимими.

Оголошення масиву

Нижче наведені приклади оголошення масивів:

int iarray[12]; //масив з дванадцяти цілих чисел

Як і при оголошенні базових змінних, оголошення масиву починається з встановлення типу даних, після цього записуються ім’я масиву, пара квадратних дужок, всередині яких записаний константний вираз, який визначає розмір масиву. Всередині квадратних дужок можна записувати тільки константи, але не ім’я змінної, для того, щоб компілятор міг визначити, який об’єм пам’яті відводити. таким чином, розмір масиву повинен бути відомий наперед і не може змінюватися під час виконання програми.

Приклади встановлення розмірів масивів:

#define iARRAY_MAX 20

#define fARRAY_MAX 15

int iarray[iARRAY_MAX]; float farray[fARRAY_MAX];

Подібне використання макроконстант дає змогу уникнути помилок, поєднаних зі зверненням до неіснуючих елементів масиву. наприклад, послідовний доступ до масиву часто організується за допомогою циклу for:

#include “afxwin.h”

#define iARRAY_MAX 20

int iarray[iARRAY_MAX];

CXXXView::OnDraw(CDC*pDC)

{ int і; for(i=0;i<iARRAY_MAX;i++){…..};}

Ініціювання масиву

Масив можна ініціювати одним з трьох способів:

  • при створенні масиву –застосовуючи ініціювання. по замовчуванні ( цей метод застосовується тільки для глобальних та статичних масивів);

  • при створенні масиву – явно вказуючи початкові константні значення;

  • при виконанні програми – шляхом запису даних у масив.

При створенні масиву можна записувати лише константні значення. пізніше у масив можна записувати і значення змінних.

Відповідно до стандарту ANSI глобальні масиви (розташовані зовні довільної функції), а також масиви, оголошені статичними всередині функції, по замовчуванні заповнюється нулями, якщо не задані початкові значення елементів масиву. Масиви вказівників заповнюються значеннями NULL. Перевірити вищесказане можна на наступному прикладі:

#include <afxwin.h>

#define iGLOBAL_ARRAY_SIZE 10

#define iSTATIC_ARRAY_SIZE 20

int iglobal_array[iGLOBAL_ARRAY_SIZE]; //

CXXXView::OnDraw(CDC*pDC){CString s;

static int istatic_array[iSTATIC_ARRAY_SIZE];

int i, k=20,L=30;

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

{s.Format(“%d”,global_array[i]);pDC->TextOut(k,20,s);k+=20;}

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

{s.Format(“%d”, static_array[i]);pDC->TextOut(L,50,s);L+=20;}

}

Після завантаження програми на екран виведуться нульові значення, надані елементам масиву по замовчуванні. Дана програма виявляє ще один суттєвий момент роботи з масивами: перший елемент масиву завжди має нульовий індекс. Це поєднано з тим, що автори мови С максимально наближували його до ассемблерних мов, де перший елемент таблиці завжди має нульове зміщення.

Явне ініціювання

Відповідно до стандарту ANSI елементам як глобальних, так і локальних масивів можна явно присвоювати початкові значення. У наступному фрагменті програми містяться оголошення чотирьох масивів, які ініціюються явно:

int iarray[3]={-1,0,1);

static float fpercent[4]={1.131579,0.75,55E0,-.33E1);

static int idecimal[3]; ={0,1,2,3,4,5,6,7,8,9};

char cvawels[] ={‘A’,’a’,’E’.’e’,’I’,’i’,’O’,’o’,’U’,’u’};

У першому рядку створюється масив iarray, що містить три цілочислових елементів, значення яких, відокремлені комами, вказані у фігурних дужках. В результаті ще при завантаженні програми резервування комірок пам’яті для масиву iarray буде супроводжуватись одночасним записом значень у ці комірки. Це дуже зручно, також ініціювання відбувається ще до початку виконання програми. Деякі компілятори дають змогу ініціювати тільки глобальних та статичних масивів, як, наприклад, у другому рядку програми.

У третьому рядку показаний приклад встановлення більшої кількості елементів масиву, ніж у ньому можна розташувати. Багато компіляторів розглядають подібну ситуацію як помилку, у той час як інші автоматично збільшують розмір масиву для розміщення додаткових елементів. Компілятор Microsoft Visual C++ повідомить про помилку типу «too many initializers” (занадто багато ініціювачів). У протилежному випадку, коли при ініціюванні вказано менше значень, ніж елементів масиву, додатковим елементам будуть надані нульові значення. З урахуванням сказаного вище, взагалі можна не вказувати розмір масиву, як це зроблено у четвертому рядку програми. Кількість значень, вказаних у фігурних дужках, автоматично визначить розмір масиву.

Розмір масиву можна задати або у квадратних дужках, або вказуючи список початкових значень при ініціюванні масиву. У більшості компіляторів вирішені обидва методи. Для прикладу розглянемо створення часто використовуваних у багатьох програмах повідомлень про помилки. Задати відповідний масив можна двома способами. Наприклад, перший:

char szinput_Errdr[41]=”Введіть значення від 0 до 9.\n”;

char szDevice_Error[18]= «Диск недоступний. .\n”;

char szMonitor_Error[49] = «Для роботи програми потрібен кольоровий монытор. .\n”;

char szWarning[36]= «Ця операція призведе до видалення файлу! \n”;

Тут потрібно попередньо порахувати кількість символів у рядку, не забуваючи до отриманої кількості додати \0, що позначує кінець рядка. Це не дуже зручно, до того ж може призвести до помилок.

Можна дати можливість компілятору автоматично обчислити розмір масиву, як показано нижче:

char szInput_Error[]=”Введіть значення від 0 до 9.\n”;

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

Доступ до елементів масиву

При оголошенні змінної відбувається резервування однієї чи кількох комірок пам’яті, а у спеціальну таблицю лексем програми записується ім’я змінної та адреса поєднаних з нею комірок. Наприклад, у наступному рядку програми резервується комірка пам’яті для зберігання цілочислового значення змінної з іменем iweekend.

int iweekеnd;

У випадку показаного нижче масиву iweek відбувається резервування не однієї, а семи комірок пам’яті, кожна з яких зберігає одне цілочислове значення.

int iweek[7];

Розглянемо, як організується доступ до однієї комірки пам’яті, поєднаної зі змінною iweekеnd, та до семи комірок пам’яті, поєднаних з масивом iweek. Для отримання значення, що зберігається у змінній, досить звернутися до цієї змінної за іменем. При доступу до мaсиву потрібно додатково вказати індекс, що є порядковим номером елементу масиву, до якого потрібно звернутися. У наступному прикладі послідовно виконується звернення до всіх елементів створеного масиву:

int iweek[0];

int iweek[1];

int iweek[6];

При зверненні до елементу масиву у квадратних дужках вказується цілочисловий індекс, що є зміщенням адреси по відношенню до базової адреси першого елементу.

Програмісти-початківці часто допускають помилку, вважаючи, що перший елемент має індекс 1. Для звернення до першого елементу масиву потрібно вказувати індекс 0, оскільки зміщення першого елементу відносно самого себе є нульовим. А, наприклад, до третього елементу потрібно звертатися за індексом 2, оскільки він на дві комірки зміщений по відношенню до першого елементу.

При роботі з масивами квадратні дужки застосовують у двох випадках. Коли відбувається оголошення масиву, число у квадратних дужках вказує кількість зарезервованих комірок пам’яті:

int iweek[7];

Коли ж виникає потреба отримати доступ до визначеного елементу масиву, тоді слідом за іменем масиву у квадратних дужках вказується індекс елементу:

iweek[3];

Для оголошеного вище масиву iweek наступний вираз присвоювання є неправильним:

iweek[7]=53219;

У масиві iweek немає елемента зі зміщенням 7 по відношенню до першого елементу, іншими словами – восьмого елементу. Оскільки масив містить тільки сім елементів, даний рядок викличе помилку. Програміст відповідає за те, щоб індекси у зверненнях до масиву мали допустимі значення.

Розглянемо наступні оголошення масиву, змінних та константи:

int iweelend=1;

int weekday=2;

та вирази:

iweek[2];

iweek[iweekday];

iweek[iweekend+iweekday];

iweek[iweekday-iweekend];

iweek[iweekend-iweekday];

Перші два рядки містять звернення до третього елементу масиву. У першому випадку індекс вказується числовою константою, а у другому випадку з цією метою використовується ім’я змінної. наступні три рядки демонструють застосування виразів для встановлення індексів. Важливо тільки, щоб результатом виразу було логічно коректне число. Наприклад, у третьому рядку отримаємо індекс 3, який вказує на четвертий елемент масиву. У четвертому рядку індекс дорівнює 1 та вказує на другий елемент масиву, а от останній рядок помилковий, оскільки значення індексу -1 недопустиме.

Для того, щоб отримати доступ до елементу масиву, немає потреби враховувати розмір відведеної для нього пам’яті, оскільки подібна робота виконується компілятором автоматично. Нехай, наприклад, потрібно отримати значення третього елемента масиву iweek, що містить цілі числа. У різних системах для відтворення даних типу int можуть використовуватися комірки пам’яті різного розміру: іноді 2 байти, іноді 4 байти. Але незалежно від системи у довільному випадку можна отримати доступ до третього елементу масиву, застосовуючи вираз iweek[2]. Індекс вказує на порядковий номер елементу у масиві незалежно від того, скільки байтів відведено кожному елементу.

Обчислення розміру масиву у байтах

Як відомо, оператор sizeof повертає розмір вказаного операнду у байтах. Його можна використовувати для змінних довільних типів, за винятком бітових полів. Часто оператор sizeof застосовують для визначення розміру змінної, тип якої у різних системах може мати різну вимірність. Як було сказано вище, в одному випадку для відтворення цілочислового значення відводиться 2 байти, в іншому – 4 байти. Тому, наприклад, при роботі з масивами, які розміщуються в пам’яті динамічно, потрібно точно знати, скільки пам’яті запитувати у операційної системи. Наступна програма автоматично обчислить та відобразить на екрані кількість байтів, відведених масиву з семи цілочислових елементів:

#include “afxwin.h”

#define iDAY_OF_WEEK 7

CXXXView::OnDraw(CDC*pDC)

{int iweek[iDAY_OF_WEEK]={1,2,3,4,5,6,7};

CString s;

s.Format(”massiv = %d byte“,(int)sizeof(iweek)); pDC->TextOut(10,10,s);}

Значення , яке повертає оператор sizeof, приводиться до типу int , тому що відповідно до стандарту ANSI даний оператор повертає значення типу size_t, оскільки у деяких системах одного лише типу int виявляється недостатнім для представлення вимірностей даних деяких типів.

Над елементами типу можна виконувати ті ж операції, що й над довільними іншими зміннними, використовувати їх у виразах, надавати значення та передавати як аргумент у функції.

Розглянемо приклад, у якому друкується розмір елементів цілочислового масиву даних.

#include “afxwin.h”

#define iDAYS 7

CXXXView::OnDraw(CDC*pDC)

{int index, iarray[iDAYS];CString s;

s.Format(” %d “,(int)sizeof(int)); pDC->TextOut(10,10,s);

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

s.Format(”<<”&iarray[%d]=”<<&iarray[index]);pDC->TextOut(10,10,s);}

}

При роботі з масивами не можна забувати про те, що індекс у зверненні до елементу не повинен перевищувати розмір масиву. Оскільки мови С та С++ створювались як альтернатива ассемблерним мовам, функції контролю подібних помилок не входили у компілятор, щоб зменшити його код. Наприклад, при компіляції наступної програми помилки не будуть виявлені, а при її виконанні можна випадково змінити значення інших змінних, і виконання програми може завершитися крахом через те, що в ній є звернення за межі масиву.

#include “afxwin.h

#define iMAX 10

#define iOOT_OF_RANGE 50

CXXXView::OnDraw(CDC*pDC)

{int inot_enough_room[iMAX], index;

for(index=0;index<iOUT_OF_RANGE; index++) inot_enough_room[index]=index; }

Масиви символів

Не дивлячись на те, що С та С++ підтримують тип даних char, у змінних цього типу можуть зберігатися тільки одиночні символи, але не рядки тексту. Якщо у програмі потрібно працювати з рядками, їх можна створювати як масиви символів. У такому масиві для кожного символу рядка відводиться власна комірка, а останній елемент містить символ кінця рядка \0.

У наступному приклад створюються три символьні масиви. Масив szvehicle1 заповнюється символ за символом за допомогою оператора присвоювання. Дані у масив szvehicle2 записуються за допомогою функції cin, а масив szvehicle3 ініціюється константним рядком.

#include “afxwin.h”

CXXXView::OnDraw(CDC*pDC)

{ char szvehicle1[7], szvehicle2[8]; CString s;

static char szvehicle3[8]=”korabel”;

szvehicle1[0]=’m’; szvehicle1[1]=’a’; szvehicle1[2]=’s’;

szvehicle1[3]=’h’; szvehicle1[4]=’i’; szvehicle1[5]=’n’

szvehicle1[6]=\0’; szvehicle2=”programma”;

s.Format(“%s %s %s” szvehicle1, szvehicle2, szvehicle3);

pDC->TextOut(10,20,s);}

Не дивлячись на те, що масив слово mashin містить 6 символів, масив szvehicle1 містить сім елементів: по одному для кожної букви та ще один для символу кінця рядка. Те ж саме справедливо і для інших двох масивів. Також масив szvehicle3 можна задати через вказування послідовності символів у фігурних дужках:

static char szvehicle3[8]={‘k’,’o’,’r’,’a’,’b’,’e’,’l’,’\0’};

Різниця полягає у тому, що тут потрібно явно вказувати символ кінця рядка, у той час як у використаному у програмі способі додавання даного символу відбувається автоматично. Оголошення масиву можна записати також наступним способом: static char szvehicle3[]=”korabel”;

При цьому розмір масиву визначається компілятором автоматично.

Багатомірні масиви

Під вимірністю масиву розуміють кількість індексів, які потрібно вказати для отримання доступу до окремому елементу масиву. Всі масиви, розглянуті досі, були одновимірними і вимагали вказування лише одного індексу. Для визначення вимірності масиву досить глянути на його оголошення. Якщо в ньому вказана лише одна пара квадратних дужок ([ ] ), тоді перед нами одновимірний масив, якщо дві ([ ] [ ]), тоді двовимірний і т.д. Масиви з більшою, ніж одна вимірність, називаються багатовимірними. У реальних програмах вимірність масиву не перевищує трьох.

#define iROWS 4

#define iCILUMNS 5

….

int irow; int icolumn; int istatus [iROWS][iCOLUMNS];

int iadd; int imultiple; CString s;int k=20,L=20;

for(irow=0;irow<iROWS; irow++)

for(icolumn=0;icolumn<iCOLUMNS;icolumn++)

{iadd=iCOLUMNS-icolimn; imultiple=irow;

istatus[irow][icolumn]=(irow+1)*icolumn+iadd*imultiple;}

for(irow=0;irow<iROWS; irow++) { s.Format(”potochni rjadok”,irow);

pDC->TextOut(k,40,s);k+=20;}

pDC->TextOut(20,60,”zmischennja vid pochatku masivu”);

for(icolumn=0;icolumn<iCOLUMNS;icolumn++) {s.Format(“%d”,istatus[irow][icolumn];pDC->TextOut(L,100,s);L+=20;}}

У програмі застосовуються два цикли for, у яких кожен елемент масиву ініціюється значенням зміщення цього елементу відносно першої комірки масиву. Створений масив має 4 рядки (константа irow) та 5 стовпців (константа icolumn), що в сумі дає 20 елементів. Багатовимірні масиви відтворюються в пам’яті комп’ютера у вигляді лінійної послідовності елементів, при цьому групування за індексами відбувається з правого боку вліво, тобто від най правішого індексу до лівого.