ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 08.09.2024
Просмотров: 16
Скачиваний: 0
Лабораторна робота № 6
Тема: Обробка двомірних динамічних масивів.
Мета: вивчити поняття «покажчик», правила створення і прийоми обробки динамічних масивів на прикладі двомірного масиву.
6. 1. Короткі теоретичні відомості Особливості застосування покажчиків
Звернення до об'єктів будь-якого типу в мові C може проводитися по імені, як ми досі робили, і по покажчику (непряма адресація).
Покажчик - це змінна, яка може містити адресу деякого об'єкту в пам'яті комп'ютера, наприклад, адреса іншої змінної. Через покажчик, встановлений на змінну, можна звертатися до ділянки оперативної пам'яті (ОП), відведеної компилято-ром під її значення.
Покажчик оголошується таким чином:
тип * ID покажчика;
Перед використанням покажчик має бути ініційований або конкретною адресою, або значенням NULL (0) - відсутність покажчика.
З покажчиками пов'язано дві унарні операції: & і *. Операція & означає «узяти адреса», а операція разадресации * - «значення, розташоване за адресою», наприклад:
int x, *y; // х - змінна типу int, у - покажчик типу int
y = &x; // y - адреса змінної x
*y = 1; // за адресою y записати 1, в результаті x = 1
При роботі з покажчиками можна використовувати операції складання, віднімання і порівняння, причому виконуються вони в одиницях того типу, на який встановлений покажчик.
Операції складання, віднімання і порівняння (більше/менше) мають сенс тільки для послідовно розташованих даних - масивів. Операції порівняння «==» і «!=» мають сенс для будь-яких покажчиків, тобто якщо два покажчики рівні між собою, то вони вказують на одну і ту ж змінну.
Зв'язок покажчиків з масивами
Покажчики і масиви тісно пов'язані між собою. Ідентифікатор масиву є покажчиком на його перший елемент, тобто для масиву int a[10], вирази a і a[0] мають однакові значення, оскільки адреса першого (з індексом 0) елементу масиву - це адреса початку розміщення його елементів в ОП.
Нехай оголошені - масив з 10 елементів і покажчик типу double :
double a[10], *p;
якщо p = a; (встановити покажчик p на початок масиву a)то наступні звернення: a[i], *(a+i) і *(p+i) еквівалентні, тобто для будь-яких покажчиків можна використовувати дві еквівалентні форми доступу до елементів масиву : a[i] і *(a+i). Очевидна еквівалентність наступних виразів :
&a[0] ( &(*p) ( p
Декларація багатовимірного масиву :
тип ID[розмір 1][розмір 2].[розмір N];
причому швидше змінюється останній індекс, оскільки багатовимірні масиви розміщуються в ОП в последователь-ности стовпців, наприклад, масив цілого типу, що складається з двох рядків і трьох стовпців (з ініціалізацією початкових значень)
int a[2][3] = 0,1,2,3,4,5;
у ОП буде розміщений таким чином:
a[0][0]=0, a[0][1]=1, a[0][2]=2, a[1][0]=3, a[1][1]=4, a[1][2]=5.
Якщо в списку ініціалізаторів даних не хапає, то відповідному елементу привласнюється значення 0.
Покажчики на покажчики
Зв'язок покажчиків і масивів з одним виміром справедливо і для масивів з бóльшим числом вимірів.
Якщо розглядати попередній масив (int a[2][3];) як масив двох масивів розмірністю по три елементи кожен, то звернення до елементу а[i][j] відповідає эквива-лентное вираження *(*(а+i)+j), а оголошення цього масиву з використанням покажчиків матиме вигляд
int **а;
Таким чином, ім'я двомірного масиву - ID покажчика на покажчик.
Динамічне розміщення даних
Для створення масивів зі змінною розмірністю використовується динамічне розміщення даних, що декларуються покажчиками.
Для роботи з динамічною пам'яттю використовуються стандартні функції бібліотеки alloc.h:
void *malloc(size) і void *calloc(n, size) - виділяють блок пам'яті розміром size і n(size байт відповідно; повертають покажчик на виділену область, при помилці - значення NULL;
void free(bf); - звільняє раніше виділену пам'ять з адресою bf.
Іншим, прийнятнішим підходом до динамічного розподілу пам'яті є використання операцій мови З++ new і delete.
Операція new повертає адресу ОП, відведеною під динамічно розміщений об'єкт, при помилці - NULL, а операція delete звільняє пам'ять.
Мінімальний набір дій, необхідних для динамічного розміщення одновимірного масиву дійсних чисел розміром n :
double *а;
. . .
а = new double[n]; // Захоплення пам'яті для n елементів
. . .
delete []а; // Звільнення пам'яті
Мінімальний набір дій, необхідних для динамічного розміщення двомірного масиву дійсних чисел розміром n(m :
int i, n, m; // n, m - розміри масиву
double **a;
a = new double *[n]; // Захоплення пам'яті під покажчики
for(i=0; i<n; i++) a[i] = new double [m]; // і під елементи
. . .
for(i=0; i<n; i++) delete []a[i]; // Звільнення пам'яті
delete []a;
Для сучасних компіляторів (версій старше «6») для звільнення пам'яті досить записати тільки delete []a;
6.2. Приклад виконання завдання
Розрахувати значення вектору _, де А - квадратна матриця розміром N(N, а Y і B - одновимірні масиви розміром N. Елементи вектору Y визначаються по формулі _.
6. 2.1. Приклад створення віконного застосування
Значення N вводити з Edit, А і B - з компонент StringGrid. Результат вивести в компоненту StringGrid.
Панель діалогу і результати виконання програми приведена на мал. 6.1.
Мал. 6.1
Налаштування компонент StringGrid
Для компоненти StringGrid1 значення ColCount і RowCount встановите рівними, наприклад, 3 - три стовпці і три рядки, а FixedCols і FixedRows - 1.
Оскільки компоненти StringGrid2 і StringGrid3 мають тільки один стовпець, то у них ColCount = 1, RowCount = 3, а FixedCols = 0 і FixedRows = 1.
У властивості Options рядок goEditing для компонент StringGrid1 і StringGrid2 встановите в положення true.
Для зміни розміру n використовується функція-обробник EditChange, отримана подвійним клацанням по компоненті Edit.
Текст програми може мати наступний вигляд:
. . .
//---------------------- Глобальні змінні -------------------
int n = 3;
double **a, *b; // Декларації покажчиків
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Edit1 ->Text=IntToStr(n);
StringGrid1 ->ColCount = n+1; StringGrid1 ->RowCount = n+1;
StringGrid2 ->RowCount = n+1; StringGrid3 ->RowCount = n+1;
// Введення в лівий верхній елемент таблиці назви масивів
StringGrid1 ->Cells[0][0] = "Матриця A";
StringGrid2 ->Cells[0][0] = "Масив B";
StringGrid3 ->Cells[0][0] = "Масив Y";
for(int i=1; i<=n;i++){
StringGrid1 ->Cells[0][i]="i="+IntToStr(i);
StringGrid1 ->Cells[i][0]="j="+IntToStr(i);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
int i;
n=StrToInt(Edit1 ->Text);
StringGrid1 ->ColCount = n+1; StringGrid1 ->RowCount = n+1;
StringGrid2 ->RowCount = n+1; StringGrid3 ->RowCount = n+1;
for(i=1; i<=n;i++){
StringGrid1 ->Cells[0][i]="i="+IntToStr(i);
StringGrid1 ->Cells[i][0]="j="+IntToStr(i);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double s;
int i, j;
a = new double*[n]; // Захоплення пам'яті під покажчики
for(i=0; i<n;i++) a[i] = new double[n]; // Захоплення пам'яті під елементи
b = new double[n];
// Заповнення масивів А і В елементами з таблиць StringGrid1 і StringGrid2
for(i=0; i<n;i++){
for(j=0; j<n;j++) a[i][j]=StrToFloat(StringGrid1 ->Cells[j+1][i+1]);
b[i]=StrToFloat(StringGrid2 ->Cells[0][i+1]);
}
// Множення рядка матриці А на вектор В і виведення результату s в StringGrid3
for(i=0; i<n;i++){
for(s=0, j=0; j<n;j++) s += a[i][j]*b[j];
StringGrid3 ->Cells[0][i+1] = FloatToStrF(s, ffFixed, 8,2);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
delete []a;
delete []b;
ShowMessage("Пам'ять звільнена"!);
Close();
}
6. 2.2. Приклад створення консольного застосування
Текст програми може мати наступний вигляд:
. . .
void main()
{
double **a, *b, s;
int i, j, n;
printf(" Input size N : "); scanf("%d",&n);
a = new double*[n]; // Захоплення пам'яті під покажчики
for(i=0; i<n;i++)
a[i] = new double[n]; // Захоплення пам'яті під елементи
b = new double[n];
puts("\n Input Massiv A :");
for(i=0; i<n;i++)
for(j=0; j<n;j++) scanf("%lf", &a[i][j]);
puts("\n Input Massiv B :");
for( i=0; i<n;i++) scanf("%lf", &b[i]);
puts("\n Massiv Y :");
for(i=0; i<n;i++){
for(s=0, j=0; j<n;j++) s+=a[i][j]*b[j];
printf(" %8.2lf ", s);
}
delete []a;
delete []b;
puts("\n Delete "!);
puts("\n Press any key ... ");
getch();
}
При введенні значень елементів масивів в одному рядку через пропуски повинен вийти наступний результат:
6.3. Індивідуальні завдання
Написати програму по обробці динамічних масивів. Розміри масивів вводити з клавіатури. При створенні віконного застосування скалярний (простий) результат виводити у вигляді компоненти Label, а масиви вводити і виводити за допомогою компонент StringGrid, в яких 0-й стовпець і 0-й рядок використовувати для відображення індексів масивів.
1. З матриці розміром N(M отримати вектор B, присвоївши його k -у елементу значення 0, якщо усі елементи k -го стовпця матриці нульові, інакше 1.
2. З матриці розміром N(M отримати вектор B, присвоївши його k -у елементу значення 1, якщо елементи k -го рядка матриці впорядковані по убуванню, інакше 0.
3. З матриці розміром N(M отримати вектор B, присвоївши його k -у елементу значення 1, якщо k -й рядок матриці симетричний, інакше значення 0.
4. Задана матриця розміром N(M. Визначити кількість «особливих» елементів матриці, вважаючи елемент «особливим», якщо він більше суми інших елементів свого стовпця.
5. Задана матриця розміром N(M. Визначити кількість елементів матриці, у яких ліворуч знаходиться елемент більше його, а справа - менше.
6. Задана матриця розміром N(M. Визначити кількість різних значень матриці, тобто елементи, що повторюються, рахувати один раз.
7. У матриці розміром N(M упорядкувати рядки за збільшенням їх перших елементів.
8. У матриці розміром N(M упорядкувати рядки за збільшенням суми їх елементів.
9. У матриці розміром N(M упорядкувати рядки за збільшенням їх найбільших елементів.