Теоретичні відомості#
Синтаксис оголошення масивів у мові С#
У загальному випадку масив — це набір елементів даних одного типу. Одновимірні масиви можна асоціювати з компонентами вектора, а двовимірні — компонентами матриці. Для оголошення масиву використовують такий синтаксис:
<тип даних> <ім’я масиву> [число елементів];
Наприклад:
int array_int[100]; /*одновимірний масив зі 100 цілочисельних елементів*/
double array_d[25]; /*одновимірний масив із 25 елементів*/
Як видно з прикладів, оголошення масивів відрізняється від оголошення звичайних змінних наявністю квадратових дужок []
. Імена масивів вибирають за тими ж правилами, що й імена змінних. Звернення до окремого елемента масиву здійснюють за номером індексу елемента. Перший елемент масиву завжди має індекс 0, другий — 1, і т.д.
Наступний фрагмент програми демонструє запис у масив значень лінійної функції f(x)
і виведення їх на екран:
Приклад 2.1#
double k = 0.5, b = 10.0;
double f[100];
for (int i = 0; i < 100; i++)
{
f[i] = k*i + b;
printf("%.2f", f[i]);
}
У мові С передбачено можливість ініціалізації масиву в момент його оголошення, наприклад, таким чином:
int powers[4] = {1, 2, 4, 6};
У цьому випадку елементу powers[0]
присвоюється значення 1, powers[1]
— 2, і т.д.
Особливістю ініціалізації масивів є те, що їхній розмір можна задавати тільки константами, а не змінними. Наприклад, наступна програма призведе до помилки під час компіляції:
int N = 100;
float array_f[N]; /*помилка, так не можна*/
Тому під час оголошення масивів зазвичай використовують такий підхід:
Приклад 2.2#
#include <stdio.h>
#define N 100
int main()
{
float array_f[N];
return 0;
}
Варто зазначити, що під час ініціалізації масивів число їхніх елементів повинно збігатися з розмірністю. Розгляньмо варіант, коли число елементів під час ініціалізації буде меншим за розмірність масиву:
#define SIZE 4
int data[SIZE] = { 512, 1024 };
for (int i = 0; i < SIZE; i++)
printf("%d, ", data[i]);
Результат роботи програми буде наступний: 512, 1024, 0, 0,
. З одержаного результату видно, що неініціалізовані елементи масиву приймаються рівними 0. У випадках, коли число елементів під час ініціалізації перевищує розмірність масиву, під час компіляції станеться помилка. Тому, коли наперед невідомо число елементів, доцільно використовувати таку конструкцію мови С:
int data[] = { 2, 16, 32, 64, 128, 256 };
У результаті ініціалізувався одновимірний масив розмірністю 6 елементів. При цьому у випадку, якщо значення індексу під час звернення до елемента масиву перевищить його розмірність, ні програма, ні компілятор не видадуть повідомлення про помилку, але при цьому в програмі можуть виникати непередбачені збої. Тому програмісту варто звертати особливу увагу на те, щоб під час звернення до елементів масиву індекси не виходили за його межі.
Для зберігання деяких видів інформації, наприклад, зображень, зручно використовувати двовимірні масиви. Оголошення двовимірних масивів здійснюють так:
int array_2D[100][20]; /*двовимірний масив зі 100 х 20 елементів*/
Нумерація елементів у цьому випадку також починається з 0, тобто array_2D[0][0]
відповідає елементу першого рядка першого стовпця, array_2D[0][1]
— елементу першого рядка другого стовпця і т.д. Для початкової ініціалізації двовимірного масиву можна використовувати таку конструкцію:
long array_2D[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
або
long array_2D[][] = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
У загальному випадку можна задати масив будь-якої розмірності. Правила роботи з такими масивами аналогічні правилам роботи з одновимірними й двовимірними масивами.
Уведення-виведення масиву#
Мова С не має вбудованих засобів для уведення-виведення масиву повністю, тому масив уводять і виводять поелементно за допомогою циклів, як, наприклад, у такій програмі:
Приклад 2.3#
#include <stdio.h>
int main(void) {
double arr[100];
int n;
// уведення масиву
printf("Уведіть кількість чисел n = ");
scanf("%d", &n);
if (n > (sizeof arr)/sizeof(double)) {
printf("Забагато елементів\n");
return 0;
}
for (int i = 0; i < n; i++) {
printf("arr[%d] = ", i);
scanf("%lf", &arr[i]);
}
/* оператори, які опрацьовують масив */
// ...
printf("\n\n");
// виведення масиву
for (int i = 0; i < n; i++)
printf("arr[%d] = %.2f\n", i, arr[i]);
}
Виведення масиву, що містить велику кількість елементів, бажано проводити в кілька рядків із зупинкою після заповнення екрана. Для цього зручно використовувати функції getch()
і clrscr()
.
Уведення двовимірного масиву здійснюють поелементно за допомогою двох укладених циклів. Наступний фрагмент програми призначено для уведення за рядками двовимірного масиву елементів типу double
розміром n
рядків на m
стовпців:
for(i = 0; i < n; i++)
for(j = 0; j < m; j++)
{
printf("a[%d][%d] = ", i, j);
scanf("%lf", &a[i][j]);
}
Для уведення масиву за стовпцями достатньо поміняти місцями рядки програми, які є заголовками циклів.
Виведення такого ж двовимірного масиву ілюструє такий фрагмент:
for(i = 0; i < n; i++)
for(j = 0; j < m; j++)
{
printf("%9.3lf", a[i][j]);
printf("\n");
}
У цьому фрагменті після виведення чергового рядка масиву здійснюється перехід на наступний рядок екрану.
Посилання та масиви#
У мові С існує настільки сильний взаємозв’язок між посиланнями й масивами, що посилання й масиви фактично потрібно розглядати одночасно. Будь-яку дію, яка досягається індексуванням масиву, можна виконати й за допомогою посилань. Варіант із посиланнями буде швидший, але він важчий для розуміння, принаймні, для початківців.
Опис int a[10]
визначає масив a розміром у 10 елементів, тобто це блок із 10 послідовних об’єктів, іменованих a[0], a[1], …, a[9]
. Запис a[i]
позначає елемент в i-й позиції від початку.
Якщо pa
— це посилання на ціле значення, описане як int* pa
, то присвоювання pa = &a[0]
установлює в pa
посилання на перший елмент a
, тобто pa містить адресу a[0]
. Присвоювання x = pa
копіює вміст a[0]
в x
.
Якщо вміст pa
вказує на окремий елемент масиву a
, то за визначенням pa + 1
указує на наступний елемент, pa - i
— на i-й елемент перед pa
, pa + i
— на i-й елемент після pa
. Таким чином, якщо pa
вказує на a[0]
, то *(pa + 1)
відповідає вмісту a[1]
, pa + i
є адресою a[i]
, a *(pa + i)
— вмістом a[i]
.
Ці міркування справедливі незалежно від типу змінних у масиві a
. Визначення операції «додавання 1 до посилання» та іншої посиланнєвої арифметики передбачає масштабування, пов’язане з розміром пам’яті для об’єкта, на який указує посилання. Таким чином, у pa + i
значення i
, перш ніж бути доданим до pa
, буде помножено на розмір об’єкта, на який указує pa
. Між індексуванням і посиланнєвою арифметикою зв’язок дуже тісний.
Будь-яку згадку масиву транслятор сприймає як посилання на початок масиву, тобто ім’я масиву є посиланнєвий вираз. Це призводить до корисних наслідків:
оскільки ім’я масиву є синонім для розташування першого елемента, то присвоювання
pa = &a[0]
можна записати й у такому вигляді:pa = a
;значення
a[i]
можна записати як*(a + i)
. Обчислюючиa[i]
, транслятор відразу переводить його в*(a + i)
. Ці форми еквівалентні;застосовуючи операцію
&
до обох частин вищенаведеної рівності, отримуємо, що&a[i]
іa + i
ідентичні:a + i
— це адреса i-го елемента відносноа
;якщо
pa
— посилання, то його можна використовувати з індексом:pa[i]
ідентично*(pa + i)
.
Іншими словами, будь-який масив та індексний вираз можна записати як посилання й зміщення, і навпаки. При цьому це можна робити навіть в одному операторі. Однак, між іменем масиву й посиланням є одна відмінність, про яку завжди варто пам’ятати. Посилання є змінна, отже pa = a
і pa++
— осмислені операції. Ім’я ж масиву — константа, а не змінна, тому конструкції на кшталт a = pa
, a++
, p = &a
неприпустимі.
Приклад 2.4#
Програма друкування вмісту одновимірного масиву з використанням посилань. Масив проініціалізовано заздалегідь.
#include <stdio.h>
#define SIZE 5
int main(void)
{
int arr[SIZE] = { 1, 2, 3, 4, 5 };
int* p;
p = arr;
for(int i = 0; i < SIZE; i++)
printf("arr[%d] = %d\n", i, *(p + i));
}
Приклад 2.5#
Програма, що реалізує заповнення двовимірного масиву випадковими елементами з використанням посилань.
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ROWS 5
#define COLUMNS 6
int main(void)
{
int arr[ROWS][COLUMNS], *p;
p = &arr[0][0];
srand(time(NULL));
for (int i = 0; i < ROWS * COLUMNS; i++)
*(p + i) = rand();
for (int i = 0; i < ROWS * COLUMNS; i++)
printf("arr[%d] = %d\n", i, *(p + i));
}