ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 22.12.2021

Просмотров: 732

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Лекція 3.

Масив в Сі. Початкові відомості.

Масив – це сукупність елементів будь-якого допустимого одного типу. Елементи масиву створюють послідовність, впорядковану по номерах їх елементів. Масиви можуть бути створені із арифметичних даних, символів, рядків структур, покажчиків файлів.

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

  1. Розмірність (кількість вимірювань) масиву визначає кількість індексів при зверненні до елементів в масиву;

  2. Розмір масиву – це кількість його елементів.

Форма обявлення масиву:

імя типу імя масиву [k1] [k2] …[kn];

де k1knкількість елементів масиву по 1..n вимірюванням. Причому значення індексів по відповідним і-вимірюванням можуть змінюватись в межах від 1 до

(ki - 1).

Наприклад:

int a [5]; - одновимірний масив на 5 значень цілого типу;

float b[10][20];- двовимірний масив із 10 рядків і 20 стовпців. Номер елементів масиву а змінюється від 0 до 4; а [0]..[4]. Для звертання до елементів масиву використовують імя масиву, після якого в квадратних дужках стоїть індекс в вигляді виразу, визначаючого значення індексу – порядкового номера елемента масиву. Допустимість значень індексів контролює програміст.

Індексів в квадратних дужках може бути стільки, скільки вимірювань в масиві; кожний індекс в своїх квадратних дужках.

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

a [i], a [i+2] – одновимірного;

b [0] [0], b [i] [j], b [9] [9] – двохвимірного;

c [i] [j] [k], c [i+2] [2*j-1] [k] – трьохвимірного масиву.

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

Початковим значенням елементам масиву можна присвоїти при його визначенні. Форма ініціалізаціїї значень для одновимірного масиву:

Тип імя – масиву [k] = {список значень};

Наприклад:

Int a[5] = {1, 5 , 3 , 4 ,2};

Float b[ ] = {1.2, 3.4, 5.4, - 2.5, 0.3, -55. };

Якщо розмір одновимірного масиву не вказаний (при визначенні маємо тільки квадратні дужки), то його визначає кількість ініційованих значень елементів масиву. В прикладі масив а[] має 5 елементів, а масив b[] – 6 елементів.

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

Наприклад:

Int a[2][4] = {{1, 2, 3, -4}, // - елементи 0-го рядка

{5, 6, -7, 8}}; // - елементи 1-го рядка

В Сі немає масивів зі змінними межами. Але, якщо кількість елементів масиву відома до виконання програми, можливо визначити його як константу за допомогою директиви # define, а потім використовувати її при визначенні границь масиву. Іменовані константи, визначені специфікатором const, не можна використовувати при визначенні границь масиву. Якщо кількість елементів масиву визначається в процесі виконання програми, використовують динамічне виділення ОП, наприклад, за допомогою функції malloc.


Наприклад:

# define m 5 // визначення значення m

main ( )

{ int a[m]; // обява масивів на m елементів

float b[m];

Масиви даних і покажчики.

Між покажчиками і масивами існує сильний взаємозвязок. Будь-яка дія над елементами масивів, яке досягається індексуванням, може бути виконана і за допомогою покажчиків (посилань) і операцій над ними.

Як показують задачі на Сі, покажчики рідко використовують зі скалярними змінними, частіше – з масивами. Покажчики дають можливість використовувати адреси приблизно так, як це робиться в ЕОМ на машинному рівні. Це дозволяє ефективно організувати роботу з масивами. Будь-яку програму, яка використовує масиви, можливо написати тільки за допомогою покажчиків.

Для роботи з масивами необхідно:

  1. Визначити ім’я масиву, його розмірність (кількість вимірювань) і розмір – кількість елементів масива.

  2. Виділити оперативну память для його розміщення.

В мові Сі можливо використовувати масиви даних будь-якого типу:

    1. Статичні: з виділенням ОП до початку виконання функції; ОП виділяється в стеку або в ОП для статистичних даних;

    2. Динамічні: ОП виділяється із кучі в процесі виконання програми за допомогою функцій malloc і alloc, розглянемо пізніше.

Розмір масиву можна не вказувати, а вказати пусті границі:

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

Static int a[ ] = {1, 2, 3}; char b[ ] = “відповідь:;

  1. Для масивів – формальних параметрів функції, наприклад:

int funl ( int a[ ], int n ); int fun2 ( int b[ ] [m] [n] );

  1. При посиланні на раніше визначенний зовнішній масив, наприклад:

int a[5]; // - визначення зовнішнього масиву

main ( )

{ extern int a[ ]; // - посилання на зовнішній масив

У всіх визначеннях масиву, в яких розмір масиву вказаний явно або неявно – це покажчик – константа.

Способи оголошення і звернення до елементів одновимірних масивів.

Звернення до елементів одновимірного масиву в загальному випадку виглядає так:

імя_масиву [ вираз ];

де імя_­масиву – покажчик – константа;

вираз – цілого типу – індекс; він визначає зміщення – приріст адреси заданого елементу масиву відносно адреси нульового елементу масиву. Елемент одновимірного масиву розташовуються в ОП підряд: нульовий, перший і т. ін.

Приклад оголошення масиву:

Int a[10]; int*p = a; // р отримав значення а

При цьому компілятор виділяє масиву в стеку ОП розміром ( sizeof ( тип ) розмір_масиву ) байт. В прикладі це 2 * 10 = 20 байт. При чому: а – покажчик – константа, адреса початку масива, його 0-го елемента; р – покажчик – змінна.

Змінній р можна присвоїти значення одним із засобів:

р = а; р = &a[0]; p = &a[ i ];

де &a[i] = = (a+i) – адреса і-го елемента масиву.

Операція sizeof повертає кількість байт, яка займає в памяті тип даних або вираз, що передається в якості параметра. Часто використовується для визначення кількості елементів масиву при опрацюванні в циклі.


У відповідності з правилами перетворення типів значення адреси і-го елемента масиву на машинному рівні формується у вигляді

&a[i] = a + i * sizeof (int);

справедливі також такі співвідношення:

&a = = a + 0 = = &a[0] - адреса а[0] – нульового елемента масива “а”;

a + 2 = = &a[2] - адреса а[2] – другого елемента масива “а”;

a + i = = &a[i] - адреса а[і] – і-го елемента масива “а”;

= = *(a+0) = = *(&a[0]) = = a[0] – значення нульового елемента масиву “а”;

*(a+2) = = a[2] - значення другого елементу масиву “а”;

*(a+i) = = a[i] - значення і-го елементу масиву “а”;

*a+2 = = a[0] + 2 - сума значень a[0] i 2;

Якщо р – покажчик на елементи типу елементів масиву а і р = а, то а і р взаємозамінні; при цьому:

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

p+2= =&a[2]= =a+2;

*(p+2)= = *(&a[2])= =a[2]= = p[2];

*(p+i)= = *(&a[i])= =a[i]= = p[i];

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

a[i], *(a+i), *(i+a), i[a];

p[i], *(p+i), *(i+a), i[p];

Приклад. Підрахувати кількість появи кожної літери в тексті.

#include <conio.h>

#include <stdio.h>

int k [256]; /*масив кількості появи кожного з 256 символів в файлі ініціалізуються 0*/

void main( )

{ int i, c; clrscr;

printf(“Введіть символи. Для закінчення введіть ^z:/n”);

while(c= getchar( ), c!=EOF) k[c]++; //або: (*(k+c))++;

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

{ printf (“k [%d]=%d-%c”, i, k [i], i); //виведення кількості символів.

іf(!(i%5)) printf (“\n”);

}

printf (“\n Для завершення програми натисніть любу клавішу \n”);

getch( );

}

В програмі використовуються:

- k – зовнішній масив на 256 елементів по кількості різних символів алфавіту; та як це зовнішній масив, він ініціюється 256 нулями;

k – покажчик-константа;

- с –значення символу, введеного з клавіатури; номер елементу в масиві к дорівнює значенню с, коду введеного символу.

Із вхідного потоку (з клавіатури) вводяться символи в змінну с до кінця файлу, тобто до появи ЕОF. Для формування поточного значення елементу масиву к використовувати один із двох варіантів виразу:

k[c]++; або (*(k+c))++;

В останньому випадку в виразі над покажчиком k спочатку виконується операція (k+r), в результаті цього, до значення покажчика k добавляється значення коду введеного символу с, а потім вибирається вміст за цією адресою: *(k+c). Після чого до вмісту добавляється 1 (для підрахування кількості символів) і результат розміщається за адресою (k+c).

Схематично це можливо представити в вигляді:

\ ( * (k + c) ) ++

&k[c] – адреса елемента масиву k[c]


*&k[c] = значення елемента k[c]



k[c]++ = ( k[c] = k[c] + 1 ); збільшення k[c] на 1.


Горізонтальні фігурні дужки обмежують частини виразів, які виконуються. Послідовність обчислень частин виразів в схемі – зверху вниз.

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


Засоби оголошення і звернення до елементів багатовимірних масивів.

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

Елементи масивів розташовуються у пам’яті таким чином, що швидше змінюються самі праві індекси, тому при звертанні до елементів масиву використовують нуль і більше індексів. Приклад оголошення двохвимірного масиву в загальному випадку int a[m] [n];

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

Наприклад:

a[0]==&a[0][0]==a+0*n*sizeof(int);

a[1]==&a[1][0]==a+1*n*sizeof(int);

a[i]==&a[i][0]==a+i*n*sizeof(int);

Приклад оголошення трьохвимірного масиву.

int a[L][m][n];

де Lкількість матриць з m рядками і n стовпцями;

m – кількість рядків(одновимірного масиву) в матриці;

n – кількість стовпців(елементів в рядку) матриці;

Цей масив складається із L матриць, кожна з яких складається із m одновимірних масивів по n елементів. При зверненні до цього масиву можна використовувати імена:

a[k][i][j] – містить 3 індекса; k – матриця, i – рядок, j – стовпці; обчислюються індексні вирази, визначається адреса елементів масиву і здобувається його значення.

a[k][i] – визначає одновимірний масив – адреса початку i-го рядка k-ї матриці;

a[k] – визначає двохвимірний масив – адресу початку k-ї матриці; тобто нульового елемента його нульового рядка;

a – адреса початку масиву, нульового елемента нульового рядка нульової матриці.

Наприклад:

int b[3][4][5];

int і, *ір, *ірр;

і=b[0][0][1]; ip=b[2][0]; ipp=b[2];

де ір, ірр – покажчики на значення типу int.

Після ір=b[2][0]; ip є покажчиком на елемент 0-рядка, 0-го стовпця 2-ї матриці масиву, тобто

b[2][0][1].

Після ірр=b[2]; ipp адресує 0-й рядок 2-ї матриці масиву, тобто має адресу b[2][0][0].

Звернення до елементів багатовимірного масиву більш детально розглянемо на прикладі двохвимірного масиву. Наприклад:

int а[3][4]; а – покажчик-константа

int *р=а; р – покажчик-змінна

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

В ОП елементи розташовані таким чином:

a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] … a[2][0] a[2][1] a[2][2] a[2][3]

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

&a=a=&a[0][0]=*aадреса a[0][0] елемента 0-го рядка 0-го стовпця матриці а;

  • **a=*(&a[0][0])=a[0][0] – значення елемента – ////

a[i]=(a+i)=*(a+i)=&a[i][0] – адреса початку i-го рядка: адреса елемента i-го рядка 0-го стовпця


  • *a[i]=**(a+i)=*(&a[i][0])=a[i][0] – значення 0-го елемента i-го рядка.

a[i][j]=*(*(a+i)+j)=*(a[i]+j)=a[i][j] – значення елемента i-го рядка, j-го стовпця.

Де (a+i)=*(a+i)=a[i] – адреса 0-го елемента i-го рядка =&a[i][0]

(*(a+i)+j) – адреса j-го елемента і-го рядка =&a[i][j].

Важливо пам’ятати, що покажчик – це змінна у=a, або у++ - допустимі операції. Ім’я масиву – константа. Тому конструкції виду a=у, а++, z=&a використовувати не можна, тому що значення константи не можна змінювати.

В мові Сі допустимі масиви покажчиків, які оголошуються таким чином:

char *m[5]; Тут m[5] – масив, який містить адресу елементів типу char.

Розглянемо програму, яка впорядковує рядки заданого масиву в порядку

зменшення кодів першої літери. Нехай в масиві S – п’ять рядків

М асив m до впорядкування







рядок 1 9

m [0]

m [1]

m [2]

m [3]

m [4]












Введемо масив m покажчиків на рядки масиву S достатньо змінити належним чином значення елементів m. Остаточно для елементів m[0],…m[4] необхідно послідовно вивести на екран рядки, на які вони показують.

Текст програми:

/*Демонстрація масиву покажчиків*/ рядок 1

#include <stdio.h>

main( )

{

int i, j,g, int [5]={0,0,0,0,0};

/*intдопоміжний масив, ініціюється нульовими значеннями*/

char p, s[5] [20], *m[5];

/*mмасив покажчиків на елементи типу char*/

printf(“Ввести пять рядків символів \n”);

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

{

scanf (“%s”, s[i]);

m[i] = &s[i] [0];

} / *масив m буде містити адреси рядків масиву S */

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

printf (“рядок - %d; адреса рядка - % p \ n”, i, m [i] );

Впорядкування рядків масиву S в порядку спадання кодів першої букви кожного рядка

/*



*/

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

{

p = ‘A’; q = 0;

/*змінній р присвоюється символ A з завчасно малим кодом (А – російська буква; в її старшому розряді записана одиниця, як у відємного числа)*/

for ( j = 0; i < 5; i++)

if (S[j] [0] > = p&& int [j] = = 0)

{

p = S[j] [0]; q = j;

}

ind [q] = 1;

/* після виконання вкладеного циклу for j дорівнює номеру рядка за найбільшим кодом першого символа; ind – допоміжний масив, який використовується виключно для повторного звернення до вибранного рядка*/

for ( j=0; j<5; j++ )

if ( S[ j ] [ 0]>=p&& ind[ j ]==0)

{

p = S [ j ] [ 0 ]; g=j;

}

ind [ g ]=1;

/*після використання вкладенногоциклу for g дорівнює номеру рядка з найбільшим кодом першого символа; ind допоміжний масив, який використовується виключно для повторного звернення до вибраного рядка */

m[i] = &S[q][0];

}

/* остаточний масив m буде містити послідовність покажчиків, невпорядковані рядки масиву S */

puts (“\ n впорядковані рядки”);

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

print f (“порядок %d \ t %s \ n”, i, m[i]);

/* виведення на екран впорядкованих рядків */