Ініціалізація покажчиків

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

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

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

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

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

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

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

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

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

int* г = р;

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

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

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

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

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

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

 

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

char* vp = (char *)0xB8000000;

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

 

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

int* suxx = NULL;

int* rulez = 0;

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

 

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

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

int* n = new int; III

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

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

за допомогою функції mallос:

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[]. Розмірність масиву при цьому не указується. Якщо квадратних дужок немає, то ніякого повідомлення про помилку не видається, але помічений як вільний буде тільки перший елемент масиву, а інші опиняться недоступні для подальших операцій. Такі елементи пам'яті називаються сміттям.

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

 

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

 

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

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

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

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

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

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

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