вів, створювання яких більш докладно розглянуто у розд. 6. Наведемо правильний код такої функції:
void print(int **А, int m, int n)
{for (int i=0; i<m; i++)
{for (int j=0; j<n; j++) cout << ' ' << А[i][j]; cout << '\n';
}}
Виклик такої функції матиме вигляд:
int **х=new int*[3];
for (i=0; i<3; i++) х[i]=new int[4]; print(х,3,4);
Приклад 5.21 Розробити програму для введення матриці розмірністю 5 5 цілих чисел, обчислення суми елементів зі значенням понад 7 і замінити у вихідній матриці ці елементи на значення 10 та обчислити нову суму елементів матриці зі значенням понад 7.
|
Вхід до sum( ) |
|
|
s = 0 |
Вхід до zamina() |
|
|
|
i 0,4 |
i 0,4 |
|
|
|
j |
0,4 |
j 0,4 |
|
|
|
Ні |
|
Ні |
|
|
matrij >7 |
|
matrij >7 |
|
Так |
|
|
Так |
|
|
|
|
s = s + matrij |
matrij = 10 |
|
|
|
Вихід |
Вихід |
|
|
|
а |
|
б |
Блок-схеми функцій:
а – sum( ); б – zamina( )
Початок
i 0,4
j 0,4
pij
s=sum(p,n)
s
zamіna(p,n)
i 0, n 1
j 0, n 1
pij
s=sum(p,n)
s
Кінець
Блок-схема основної програми (кнопка “Розв‟язок”)
Тексти функцій і програми для командної кнопки “Розв‟язок”: int sum(int matr[5][5])
{int i, j, s=0;
for (i=0; i<5; i++) for (j=0; j<5; j++)
if (matr[i][j] > 7) s += matr[i][j];
return s;
}
//--------------------------------------------
void zamіna(int matr[5][5])
{for (int i=0; i<5; i++)
for (int j=0; j<5; j++) if (matr[i][j] > 7) matr[i][j] = 10;
}
//--------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{int i, j, s, p[5][5]; for (i=0; i<5; i++)
for (j=0; j<5; j++) p[i][j]=StrToInt(SG1->Cells[j][i]); s=sum(p);
Edit1->Text=IntToStr(s); zamіna(p);
for (i=0; i<5; i++)
for (j=0; j<5; j++) SG2->Cells[j][i]=IntToStr(p[i][j]); s=sum(p);
Edit2->Text=IntToStr(s);
}
Приклад 5.22 Розробити схему алгоритму та програмний проект для:
обчислення елементів матриці |
розмірністю 4 4 за формулою |
Аij = sin(i) + ln(j2), де i |
|
|
|
|
|
0,4 |
, j 0,4 ; |
|
обчислення елементів вектора добутків елементів головної діагоналі матриці на елементи другого рядка;
переставляння елементів неголовної діагоналі й останнього стовпчика
матриці.
Розв‟язок. Схеми алгоритмів функцій та основної програми, а також приклад вигляду форми з результатами роботи наведено нижче.
Для даного прикладу програми доцільно буде змінні, використовувані в різних функціях, оголосити глобально. В такому разі не треба буде передавати їхні значення у якості параметрів функцій, оскільки ці значення видимі в усіх функціях програмного проекту.
|
|
|
|
|
|
Масиви в С++ |
201 |
|
|
|
|
|
|
|
|
|
|
Вхід до New_Matr() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Вхід до Matr() |
|
|
|
|
|
i 0,3 |
|
|
|
|
|
|
Вхід до Vector() |
|
|
|
|
|
|
|
i 0,3 |
|
|
|
|
|
|
|
|
tmp=A[i][3-i] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i 0,3 |
|
|
|
|
|
|
|
j 0,3 |
|
|
|
|
|
|
|
|
A[i][3-i]=A[i][3] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
V[i]=A[i][i]*A[1][i] |
|
|
|
|
|
|
|
|
Аij=sin(i+1)+ln(j+1) |
|
|
|
|
|
|
A[i][3]=tmp |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Вихід |
Вихід |
|
б |
в |
|
Блок-схеми функцій: |
|
|
Matr(); б – Vector(); в – New_Matr() |
|
|
|
Початок |
|
Початок |
Виклик |
|
|
|
Vector() |
функції |
|
i 0,3 |
|
|
|
i 0,3 |
i 0,3 |
|
|
|
Виведення |
Виведення |
|
V[i] |
А[i][j] |
|
Кінець |
Кінець |
|
|
|
б |
а |
Блок-схеми для кнопок:
а – Button1 та Button3; б – Button2
Тексти функцій і програм для усіх командних кнопок:
int i, j; float A[4][4], V[4];// Глобально оголошені змінні //-----------------------------------------------------------------
void Matr() // Функція обчислення елементів матриці
{for (i=0; i<4; i++)
for (j=0; j<4; j++) A[i][j]=sin(i+1)+log((j+1)*(j+1));
}
//-----------------------------------------------------------------
void Vector() // Функція обчислення елементів вектора
{ for (i=0; i<4; i++) V[i]=A[i][i]*A[1][i];
}
//-----------------------------------------------------------------
// Функція переставляння елементів неголовної діагоналі й останнього стовпчика матриці void New_Matr()
{float tmp;
for (i=0; i<4; i++)
{tmp = A[i][3-i]; A[i][3-i] = A[i][3];
A[i][3] = tmp;
}
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)//Обчислити матрицю
{ Matr(); // Виклик функції for (i=0; i<4; i++)
for (j=0; j<4; j++) StringGrid1->Cells[j][i]=FormatFloat("0.000", A[i][j]);
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)//Обчислити вектор
{ Vector(); // Виклик функції for (i=0; i<4; i++)
StringGrid2->Cells[i][0]=FormatFloat("0.000", V[i]);
}
//-----------------------------------------------------------------
// Обміняти місцями неголовну діагональ і останній стовпчик
void __fastcall TForm1::Button3Click(TObject *Sender) { New_Matr(); // Виклик функції
for (i=0; i<4; i++) for (j=0; j<4; j++)
StringGrid3->Cells[j][i]=FormatFloat("0.000", A[i][j]);
}
Приклад 5.23 Розробити програму для введення матриці цілих чисел розмірністю 6 3 і обчислення елементів вектора середніх арифметичних непарних елементів парних рядків.
Розв‟язок організуємо в окремій функції Vektor(). Для обчислення елементів вектора спочатку слід організувати цикл для переміщення по парних рядках (2, 4, 6). У кожному з парних рядків, рухаючись по стовпчиках, підсумовуємо тільки парні елементи та їхню кількість. Перед обчисленням середнього арифметичного, тобто перед діленням, перевіряємо можливість відсутності в даному парному рядку непарних елементів, тобто виключаємо поділ на нуль. Змінна k є потрібна для послідовного формування індексів вектора, оскільки їхня нумерація не збігається з нумерацією рядків у матриці.
Текст функції та програми для командної кнопки: void Vektor(int M[6][3], float X[3])
{int i, j, kol, k = 0; for(i=1; i<6; i+=2)
{X[k] = kol = 0; for(j=0; j<3; j++)
if(M[i][j] % 2 == 1)
{X[k] += M[i][j]; kol++;
}
if(kol) X[k] /= kol ; else X[k] = 0;
k++;
}
}
//----------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click (TObject *Sender) { int i, j, A[6][3]; float V[3];
for(i=0; i<6; i++) |
|
for(j=0; j<3; j++) |
|
A[i][j] = StrToInt(SG1->Cells[j + 1][i + 1]); |
Vektor(A, V); |
// Виклик функції |
for(i=0; i<3; i++)
SG2->Cells[0][i + 1] = FloatToStr(V[i]);
}
//----------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{int i, j;
for(i=1; i<=6; i++) SG1->Cells[0][i]=IntToStr(i)+"-й рядок"; for(j=1; j<=3; j++) SG1->Cells[j][0]=IntToStr(j)+"-й стовпчик"; SG2->Cells[0][0] = "Вектор";
randomize(); for(i=1; i<=6; i++)
for(j=1; j<=3; j++) SG1->Cells[j][i]=IntToStr(50-random(100));
}
Питання та завдання для самоконтролю
1)Дайте означення масиву у програмуванні?
2)В який спосіб задають розмірність масиву?
3)Яке призначення індексів масиву? Змінні яких типів можна використовувати для індексів? В який спосіб записують індекси масиву?
4)Які з наведених оголошень одновимірного масиву з 10-ти елементів помилкові і чому?
а) int A[1..10]; |
в) int A[9]; |
б) float A[10]; |
г) float A[0, 9]; |
5)Запишіть оголошення одновимірного масиву з 25-ти дійсних чисел.
6)Наведіть оператор, який присвоює першому елементу масиву A з 20ти цілих чисел нульове значення.
7)Запишіть оголошення одновимірного констант-масиву з послідовності символів Вашого прізвища.
8)Які компоненти C++ Builder для введення елементів одновимірних масивів на форму Ви знаєте?
9)Запишіть оператор оголошення символьного масиву S з 10-ти елеме-
нтів.
10) Запишіть оператори виведення масиву W з 17-ти дійсних чисел до трьох різних компонентів.
11) Оберіть правильні твердження:
а) якщо список ініціалізації містить початкових значень менше за розмірність масиву, це є помилка;
б) оголошення int A[23]; резервує пам‟ять під масив А з 23 елементів цілого типу;
в) оголошення float A = {-1, 2.2, 5}; резервує пам‟ять для трьох елементів масиву A типу float;
г) масив може зберігати дані різних типів.
12) В яких випадках при оголошенні масиву можна не задавати його розмірність?
13)Віднайдіть і розтлумачте помилки у твердженнях: а) індекси масиву можуть бути якого завгодно типу;
б) при оголошенні масиву програма автоматично ініціалізує його нульовими значеннями;
в) щоб звернутися до конкретного елемента масиву, слід записати ім‟я масиву і значення цього елемента;
г) кількість елементів масиву завжди збігається з кількістю займаних ним байтів оперативної пам‟яті.
14)Оберіть ім‟я змінної, значенням якої є сума елементів масиву в наведених трьох фрагментах програм:
а) s1=0; |
б) s2=1; |
в) s3=0; |
for(i=0;i<10;i++) |
for(i=0;i<10;i++) |
for(i=0;i<10;i++) |
s1 += a[i]; |
s2 *= a[i]; |
if(a[i]>0) s3++; |
|
Масиви в С++ |
|
205 |
15) Запишіть ім‟я змінної, значенням якої є кількість додатних елементів |
масиву в наведених трьох фрагментах програм: |
|
|
а) S1=0; |
б) S2=1; |
в) S3=0; |
for(i=0;i<10;i++) |
for(i=0;i<10;i++) |
|
for(i=0;i<10;i++) |
S1 += a[i]; |
S2 *= a[i]; |
|
if(a[i]>0) S3++; |
16) Запишіть ім‟я змінної, значенням якої є максимальний з елементів ма- |
сиву в наведених трьох фрагментах програм: |
|
|
а) S1=0; |
б) S2=a[0]; |
|
в) S3=0; |
for(i=0;i<10;i++) |
for(i=1;i<10;i++) |
|
for(i=0;i<10;i++) |
S1 += a[i]; |
if(a[i]>S2)S2=a[i]; |
if(a[i]>0)S3=S3+1; |
17) Які з наведених оголошень двовимірних масивів неправильні й чому?
а) int С[1..5, 1..5]; |
в) float С[1..5][1..5]; |
б) float C[5][5]; |
г) int C: [5][5]; |
18)В який спосіб розміщуються елементи двовимірних масивів в оперативній пам‟яті?
19)Запишіть фрагмент програми для введення з компонента StringGrid1 елементів матриці W з 3-х рядків і 5-ти стовпчиків цілих чисел.
20)Задайте при оголошенні матриці цілих чисел z з 3-х рядків і 2-х стовпчиків початкові значення її елементам.
21)Запишіть оператор оголошення символьної матриці S розміром 7 3.
22)Під яким номером записано оператор, що обчислює суму елементів
головної діагоналі матриці A цілих чисел розміром 5 5?
а) for(i=0, s=0; i<5; i++) s++;
б) for(i=0, s=0; i<5; i++) s+=A[i][i];
в) for(i=0, s=0; i<5; i++) for(j=0; j<5; j++) s+=A[i][j]; г) for(i=0, s=0; i<5; i++) A[i][i]=0;
23)Запишіть фрагмент програми обчислення кількості елементів масиву A
з10-ти цілих чисел, значеннями яких є двоцифрові числа.
24)Запишіть фрагмент програми для обчислення середнього арифметич-
ного, максимального і мінімального елементів масиву Z з 15-ти цілих чисел.
25)Оберіть правильні оголошення масивів:
а) mas[7]={1,3,0,-2,0,1,9}; б) char s[10];
в) const int m=9; int A[m]; г) float C[5]={12,4,82};
д) int B[]={1,20,3,0,-1};
е) int W[5]={-1,2,40,8,20,9};
26) Оберіть номер правильного оператора виведення на екран у консольному режимі масиву mas з 7-ми цілих чисел:
а) cout << mas;
б) for(int i=0; i<7; i++) cout << mas[i]; в) for(int i=0; i<=7; i++) cout << mas[i]; г) for(int i=1; i<7; i++) cout << mas[i]; д) for(int i=1; i<=7; i++) cout << mas[i];
27) Віднайдіть і розтлумачте помилки у фрагментах програм для int s, i, a[10];:
а) for(s=0, i=0; i<10; i++) { cin >> a[i]; s += a[i];} cout << "Середнє арифметичне введених чисел =" << s/10;
б) for(i=0; i<10; i++) { cin >> a[i]; s += a[i];} cout << "Сума елементів масиву = " << s;
в) for(s=0, i=0; i<5; i+=2) { cin >> a[i]; s += a[i];} cout << "Сума непарних елементів масиву =" << s/5.;
г) for(s=0, i=0; i<5; i++) { cin >> a[i]; s *= a[i];} cout << "Добуток елементів масиву = " << s;
27)Запишіть фрагмент програми обчислення остачі від ділення максимального на мінімальний елемент масиву з 10-ти цілих чисел.
28)Запишіть фрагмент програми упорядкування за спаданням масиву з 20-ти цілих чисел.
29)Чи можна виконувати опрацювання двовимірного масиву, організувавши зовнішній цикл по стовпчиках, а внутрішній – по рядкам?
30)Чи варто організовувати вкладені цикли для опрацювання лише головної діагоналі квадратної матриці?
Розділ 6
Вказівники. Динамічна пам'ять
6.1 Вказівники
Пам‟ять комп‟ютера являє собою сукупність комірок для зберігання інформації, кожна з яких має власний номер. Ці номери називаються адресами. Розмір однієї комірки пам‟яті становить 1 байт. При оголошенні змінної в пам‟яті виділяється ділянка обсягом, відповідним до типу змінної, наприклад 4 байти для типу int. Ця ділянка пам‟яті пов‟язується з іменем змінної. Можна сказати, що адреса змінної – це адреса першої комірки цієї ділянки. Існують спеціальні змінні – вказівники, в яких можна зберігати адреси комірок пам‟яті, тобто адреси змінних.
Слід звернути увагу на те, що адреса і значення змінної – це зовсім різні поняття. Зазвичай адреси змінних не є важливими, найголовніше – значення цих змінних. Вказівники використовуються для прямого доступу до даних у пам‟яті.
Адреса |
Значення |
|
комірки |
змінної |
|
пам‟яті |
в пам‟яті |
|
1000 |
|
Комірки з адресами 1000-1003 |
|
зберігають значення 45016, |
|
|
1001 |
|
яке є адресою іншої комірки |
|
|
|
45016 |
|
1002 |
|
|
1003 |
|
|
1004 |
|
|
|
|
1005 |
|
|
|
|
1006 |
|
|
|
|
|
|
|
|
. |
|
|
. |
|
|
. |
|
|
Пам‟ять |
Вказівник оголошується за допомогою *:
<тип> *<ім‟я>;
Тут тип – це базовий тип вказівника, котрим може бути який завгодно тип. Ім‟я є ідентифікатором змінної-вказівника. Слід звернути увагу на те, що тип – це не тип вказівника, а тип даних, адресу яких буде записано у вказівнику.
Наприклад, вказівник на пам‟ять, в якій зберігатиметься ціле число: int *А;
Вказівник на дійсне число:
float *В;
При цьому змінна А – це адреса ділянки пам‟яті, в якій розташоване ціле число, B – адреса ділянки пам‟яті, в якій розташоване дійсне число. Обидві змінні – А та B – є вказівниками, тобто їхніми значеннями є адреси. Окрім того, адреси змінних типу int та float займають у пам‟яті однакову кількість байтів. Але насправді змінні А та В мають різний тип: А – вказівник на ціле, B – вказівник на дійсне.
Базовий тип вказівника визначає тип об‟єкта, адресу якого містить вказівник. Фактично вказівник будь-якого типу може посилатися на яке завгодно місце в пам‟яті. Однак операції, які можуть виконуватися з вказівником, суттєво залежать від його типу. Наприклад, якщо оголошено вказівник типу int *, компілятор припускає, що адреса, на яку він посилається, містить змінну типу int, хоча це може бути і не так. Отже, при оголошенні вказівника слід переконатися, що його тип є сумісним з типом об‟єкта, на який він буде посилатися.
Вказівникові можна присвоїти лише адресу змінної відповідного типу, оскільки С++ не виконує автоматичного перетворення типів вказівників. Тому такі команди є помилковими:
A = B; // Не можна переприсвоювати один одному вказівники на різні типи;
А= 10; // не можна присвоювати числа вказівникам.
Умові С++ визначено дві операції для роботи з вказівниками: 1) * – розадресація, чи розіменування вказівника;
2) & – адресація, тобто отримання адреси змінної.
Для прикладу у програмі оголосимо цілу змінну Х зі значенням 7: int Х = 7;
тоді, щоб її адресу записати до вказівника А, слід записати команду:
А = &Х; // Значенням змінної А є адреса змінної Х.
Якщо пам‟ять, у якій зберігається змінна Х, починається з комірки під номером 2000, тоді змінній А після цієї команди буде присвоєно значення 2000.
Тепер до значення 7, яке зберігається у змінній X, можна звернутися не лише за ім‟ям X, але й через вказівник. При цьому слід застосувати операцію розадресації вказівника (вона позначається символом “*”), наприклад: *А. Цей запис означає: “значення, яке зберігається за адресою, записаною у змінній A”. Щоб збільшити на 2 оголошену раніше змінну X, можна записати так:
X = X + 2; /*За ім‟ям X комп‟ютер визначить адресу ділянки пам‟яті, в якій вона розташована, візьме значення, що зберігається у пам‟яті за цією адресою, збільшить його на 2 і запише назад у ту ж саму ділянку пам‟яті */
чи так:
*A = (*A) + 2;/* Комп‟ютер з ділянки пам‟яті за адресою, записаною у змінній A, візьме значення, яке зберігається в ній, збільшить це значення на 2 і запише в ту ж саму ділянку */
У першому разі відбувається звертання до змінної з ім‟ям X, у другому – до змінної через її адресу, яку записано у змінній-вказівнику A.