Покажчики найчастіше використовують при роботі з динамічною пам'яттю, званою деякими естетами купою (переклад з англійської мови слова 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) в останню чергу інтерпретується специфікатор типу.