Модуль 4 Обробка лінійних масивів. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Модуль 4 Обробка лінійних масивів.



 

Вказівники.

Ініціалізація вказівників.

Масиви та вказівники.

Вказівники.

 

Коли компілятор обробляє оператор визначення змінної, наприклад, int i=10;, він виділяє пам'ять відповідно до типу (int) і ініціалізує її зазначеним значенням (10). Всі обіги в програмі до змінного по її імені (i) заміняються компілятором на адресу області пам'яті, у якій зберігається значення змінної. Програміст може визначити власні змінні для зберігання адрес областей пам'яті. Такі змінні називаються покажчиками.

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

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

тип (*ім'я) (список_типів_аргументів):

Наприклад, оголошення:

    int (*fun) (double, double);

задає покажчик з ім'ям fun на функцію, що повертає значення типу int і має два аргументи типу double.

Покажчик на об'єкт містить адреса області пам'яті, у якій зберігаються дані певного типу (основного або складеного). Найпростіше оголошення покажчика на об'єкт (надалі називаного просто покажчиком) має вигляд:

   тип *ім'я;

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

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

    int *a, b, *c;

описуються два покажчики на ціле з іменами a й c, а також ціла змінна b. Розмір покажчика залежить від моделі пам'яті. Можна визначити покажчик на покажчик і т.д.

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

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

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

    int i;                                //ціла змінна

    const int ci=1;                 //ціла константа

    int *pi;                            //покажчик на цілую змінну

    const int * pci;                // покажчик на цілу константу

    int * const cp=&i;           //покажчик-константа на цілую змінну

    const int * const cpc=&ci; //покажчик-константа на цілу константу

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

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

Ініціалізація вказівників.

 

Покажчики найчастіше використаються при роботі з динамічною пам'яттю, називаної деякими естетами купою (переклад з англійської мови слова heap). Це вільна пам'ять, у якій можна під час виконання програми виділяти місце відповідно до потреб. Доступ до виділених ділянок динамічної пам'яті, називаним динамічними змінними, виробляється тільки через покажчики. Час життя динамічних змінних - від крапки створення до кінця програми або до явного звільнення пам'яті. У З++ використається два способи роботи з динамічною пам'яттю. Перший використає сімейство функцій malloc і дістався в спадщину від З, другий використає операції new й delete.

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

Існують наступні способи ініціалізації покажчика:

1. Присвоювання покажчику адреси існуючого об'єкта:

· за допомогою операції одержання адреси:

int a=5;        //ціла змінна

int * p=&a; //у покажчик записується адреса а

int * p (&a); //тим же саме іншим способом

· за допомогою значення іншого ініціалізованого покажчика:

int * r=p;

· за допомогою іменами масиву або функції, які трактуються як адреса:

int b[10];                         //масив

int * t=b;                         //присвоювання адреси початку масиву

void f(int5 a){/*...*/}    //визначення функції

void (*pf)(int);               //покажчик на функцію

pf=f;                               //присвоювання адреси функції

2. Присвоювання покажчику адреси області пам'яті в явному виді:

Char * vp=(char )0 хВ8000000;

Тут 0хв000000 - шістнадцаятирічна константа, (char *) - операція приведення типу: константа перетвориться до типу «покажчик на char».

3. Присвоювання порожнього значення:

int * suxx=NULL;

int * rulez=0;

У першому рядку використається константа NULL, певна в деяких заголовних файлах С як покажчик, дорівнює нулю. Рекомендується використати просто 0, тому що це значення типу int буде правильно перетворено стандартними способами відповідно до контексту. Оскільки гарантується, що об'єктів з нульовою адресою ні, порожній покажчик можна використати для перевірки, посилається покажчик на конкретний чи об'єкт ні.

4. Виділення ділянки динамічної пам'яті й присвоювання її адреси покажчику:

· за допомогою операції new:

int * n=new int;                               //1

int * m=new int (10);                      //2

int * q=new int [10];                       //3

· c допомогою функції malloc:

int * u=(int *)malloc(sizeof(int));  //4

В операторі 1 операція new виконує виділення достатнього для розміщення величини типу int ділянки динамічної пам'яті й записує адресу початку цієї ділянки в змінну n. Пам'ять під саму змінну n (розміру, достатнього для розміщення покажчика) виділяється на етапі компіляції.

В операторі 2, крім описаних вище дій, виробляється ініціалізація виділеної динамічної пам'яті значенням 10.

В операторі 3 операція new виконує виділення пам'яті під 10 величин типу int (масиву з 10 елементів) і записує адресу початку цієї ділянки в змінну q, що може трактуватися як ім'я масиву. Через ім'я можна звертатися до будь-якого елемента масиву.

Якщо пам'ять виділити не вдалося, по стандарті повинне породжуватися виключення bad_alloc. Старі версії компіляторів можуть повертати 0.

В операторі 4 робиться те ж саме, що й в операторі 1, але за допомогою функції виділення пам'яті malloc, успадкованої з бібліотеки С. У функцію передається один параметр - кількість виділюваної пам'яті в байтах. Конструкція (int) використається для приведення типу покажчика, що повертає функцією, до необхідного типу. Якщо пам'ять виділити не вдалося, функція повертає 0.

Операцію new використати переважніше, ніж функцію malloc, особливо при роботі з об'єктами.

Звільнення пам'яті, виділеної за допомогою операції new, повинне виконуватися за допомогою delete, а пам'яті, виділеною функцією malloc - за допомогою функції free. При цьому змінн-покажчик зберігається й може ініціалізується повторно. Наведені вище динамічні змінні знищуються в такий спосіб:

   delete n; delete m; delete [] q; free (u);

Якщо пам'ять виділялася за допомогою new [], для звільнення пам'яті необхідно застосовувати delete []. Розмірність масиву при цьому не вказується. Якщо квадратних дужок ні, то ніякого повідомлення про помилку не видається, але позначений як вільний буде тільки перший елемент масиву, а інші виявляться недоступні для подальших операцій. Такі комірки пам'яті називаються сміттям.

Увага!

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

3. Масиви та вказівники

За допомогою комбінацій зірочок, круглих і квадратних дужок можна описувати складені типи й покажчики на складені типи, наприклад, в операторі

int *(*p[10])();

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

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

При інтерпретації складних описів необхідно дотримуватися правила «зсередини назовні»:

1. якщо праворуч від імені є квадратні дужки, це масив, якщо дужки круглі - це функція;

2. якщо ліворуч є зірочка, це покажчик на проінтерпретовану раніше конструкцію;

3. якщо праворуч зустрічається закриваюча дужка, необхідно застосувати наведені вище правила усередині дужок, а потім переходити назовні;

4. в останню чергу інтерпретується специфікатор типу.

Для наведеного вище опису порядок інтерпретації зазначений цифрами:

 int *(*p[10]) ();

5 4 2 1 3        //порядок інтерпретації опису

___________________________________________________________________

 

Динамічні масиви.

 Динамічні масиви.

Виділення ділянки динамічної пам'яті (і присвоювання її адреси покажчику) можна здійснити такими способами:

· за допомогою операції new:

int * n=new int;                               //1

int * m=new int (10);                      //2

int * q=new int [10];                       //3

· c допомогою функції malloc:

int * u=(int *)malloc(sizeof(int));  //4

В операторі 1 операція new виконує виділення достатнього для розміщення величини типу int ділянки динамічної пам'яті й записує адресу початку цієї ділянки в змінну n. Пам'ять під саму змінну n (розміру, достатнього для розміщення покажчика) виділяється на етапі компіляції.

В операторі 2, крім описаних вище дій, виробляється ініціалізація виділеної динамічної пам'яті значенням 10.

В операторі 3 операція new виконує виділення пам'яті під 10 величин типу int (масиву з 10 елементів) і записує адресу початку цієї ділянки в змінну q, що може трактуватися як ім'я масиву. Через ім'я можна звертатися до будь-якого елемента масиву.

Якщо пам'ять виділити не вдалося, по стандарті повинне породжуватися виключення bad_alloc. Старі версії компіляторів можуть повертати 0.

В операторі 4 робиться те ж саме, що й в операторі 1, але за допомогою функції виділення пам'яті malloc, успадкованої з бібліотеки С. У функцію передається один параметр - кількість виділюваної пам'яті в байтах. Конструкція (int) використається для приведення типу покажчика, що повертає функцією, до необхідного типу. Якщо пам'ять виділити не вдалося, функція повертає 0.

Операцію new використати переважніше, ніж функцію malloc, особливо при роботі з об'єктами.

Звільнення пам'яті, виділеної за допомогою операції new, повинне виконуватися за допомогою delete, а пам'яті, виділеною функцією malloc - за допомогою функції free. При цьому змінн-покажчик зберігається й може ініціалізуватися повторно. Наведені вище динамічні змінні знищуються в такий спосіб:

   delete n; delete m; delete [] q; free (u);

Якщо пам'ять виділялася за допомогою new [], для звільнення пам'яті необхідно застосовувати delete []. Розмірність масиву при цьому не вказується. Якщо квадратних дужок ні, то ніякого повідомлення про помилку не видається, але позначений як вільний буде тільки перший елемент масиву, а інші виявляться недоступні для подальших операцій. Такі комірки пам'яті називаються сміттям.

 

Модуль 5 Обробка матриць.

 

Динамічні матриці.

Рядки та символи.

Динамічні матриці.

 

Для створення динамічного багатомірного (двовимірного) масиву необхідно вказати в операції new всі його розмірності, причому тільки сама його ліва розмірність може бути змінної (у матриці це - у рядків).

int nstr=5;              

int **m=(int **) new int [nstr][10]; //це матриця nstr-рядків й 10 стовпців.

Більше універсальний спосіб (і безпечний) виділення пам'яті під матрицю, коли обидві розмірності - змінні:

int nstr, nstb;              

cout << "Уведіть кількість рядків і стовпців:";

   

cin >> nstr >>nstb;

           

int **a= new int *[nstr]; //створення покажчика на покажчик int і виділяється                                         

                                       //пам'ять під nstr рядків

     

for (int i= 0; i,=nstr; i++)   //виділяється пам'ять під кожен рядок матриці

 { a[i] = new int [nstb];   //кожному елементу масиву привласнюється

 

  }                            //адреса початку рядка матриці (усього nstb

                                  //елементів у рядку)

     

 

  int **a  

int *a[nstr]

int a[nstr][nstb];

   
       

 

 

   
          0 1 nstb-1
  a 0 a[0]   a[0][0] a[0][1] a[0] [nstb-1]
    1 a[1]   a[1][0] a[1][1] a[1] [nstb-1]
       
    nstr-1 a[nstr-1]   a[nstr-1] a[nstr-1] a[nstr-1] [nstb-1]
                 

Тоді для звертання до елемента матриці i-ої рядка j-го стовпця через покажчик необхідно вказати *(*(a+i)+j).

Звільнення пам'яті - delete [].

 



Поделиться:


Последнее изменение этой страницы: 2021-12-15; просмотров: 40; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.128.190.102 (0.051 с.)