
- •1. Клас пам'яті, час існування та видимість об'єкта
- •8. Стек і функції (обробка рекурсивних викликів).
- •11. Поняття показника.
- •12.Операція непрямого звернення до показників, розіменування.
- •13. Особливості роботи з порожніми показниками (типу void).
- •16. Поняття витоку пам’яті при роботі з показниками.
- •20. Варіанти оголошень посилальних параметрів
1. Клас пам'яті, час існування та видимість об'єкта
Кожен об’єкт програми (змінна, функція,...) має свій тип і клас пам’яті. Тип визначає обсяг пам’яті для об’єкта і операції, що можуть виконуватись над об’єктом. Клас пам'яті задає місце розташування об'єкта в оперативній пам'яті та встановлює для нього час існування, тобто час, протягом якого об‘єкт зберігається в оперативній пам‘яті, і область видимості, яка визначає ту частину програми, де можна використовувати цей об‘єкт. На відміну від типу, клас пам’яті можна явно не вказувати, тоді він встановлюється компілятором за місцем оголошення об'єкта.
За часом існування об‘єкти поділяють на три групи: глобальні (або статичні) – існують протягом усього часу виконання програми; локальні – існують лише при виконанні блоку, де вони оголошені; динамічні – час існування змінюється програмою. Глобальні дані зберігаються в окремій області оперативної пам’яті – сегменті даних. Локальні дані – в області, що називається стеком. Динамічні дані – в області динамічної пам’яті. За правилами замовчування змінні, описані всередині блоку, та формальні параметри функцій мають локальний час існування. Змінні, описані зовні всіх блоків (між функціями) мають глобальний час існування. ( Блоком вважається тіло функції, а також внутрішня група описів у фігурних дужках) Функції в С – програмах можна описувати тільки на зовнішньому рівні, тому всі функції мають
глобальний час існування. За областю видимості (або областю дії) об‘єкти ділять на три групи: глобальні – видимі в межах усієї програми; частково глобальні – видимі в межах одного програмного файла; локальні – видимі в блоці, де оголошено даний об‘єкт. Специфікатори застосовують тільки тоді, коли потрібно змінити стандартний клас пам‘яті об‘єкта, інакше він встановлюється за правилами замовчування.
Синтаксис: специфікатор_класу_пам‘яті тип ім‘я_змінн
Модифікатор |
застосування |
Оласть дії |
Час життя |
auto |
локальне |
блок |
тимчасовий |
register |
локальне |
блок |
тимчасовий |
extern |
глобальне |
блок |
тимчасовий |
static |
Локальне/ глобальне |
Блок/файл |
постійний |
volatile |
глобальне |
Блок/файл |
постійний |
Специфікатори локальних змінних:
auto - клас за замовчуванням; register - зберігання змінної у регістрі процесора, дає змогу істотно скоротити час звертання до змінних, за відсутності вільних регістрів змінна буде опрацьовуватись як змінна з класом auto; така змінна не має адреси; static - статичні змінні існуються протягом усього часу виконання програми, проте областю їх дії залишається той блок, у якому вони оголошені; extern - змінна є посиланням на глобальну змінну з тим самим іменем і типом, описану далі в тексті програми або в іншому програмному файлі.
Специфікатори глобальних змінних: static - областю дії даної змінної буде частина програми від точки опису до кінця файлу; в інших файлах програми ця змінна недоступна; extern - змінні є посиланнями на відповідні зовнішні змінні та роблять ці змінні видимими (оголошеними) в межах поточного файлу. Voletile – в тих випадках коли потрібно передбачити модифікацію змінної периферійними пристроєм або іншою програмою
2. Автоматичні змінні. Модифікатор auto використовується для опису локальних змінних. Оскільки для локальних змінних він використовується по замовчуванню, то на практиці його частіше всього опускають.
# includemain ()
{
AutointmyVal= 1;
Cout<<myVal;
Return 0;
}
Модифікатор auto використовується тільки для локальних змінних, які видно в блоці і при виході з блоку такі змінні знищуються. Регістрові змінні. Модифікатор register дозволяє компілятору намагатись розмістити дану змінну в регістрі процесора . Якщо така спроба закінчується невдало, то змінна веде себе, як локальна змінна типу auto. Розміщення змінних в регістрах оптимізує програмний код по швидкості, так як процесор оперує із змінними, що знаходяться в регістрах значно швидше ніж з пам’яттю, але у зв’язку з тим, що число регістрів процесора обмежено, то і к-сть таких змінних може бути малою. Модифікатор цього типу використовується тільки для локальних змінних. Якщо програма складається з декількох модулів, то деякі змінні можуть використовуватися для передачі значень з одного файлу в інший. При чому в одному модулі вона об’являється, якглобальна, а у інших із використанням модифікатора extern. Якщо об’ява зовнішньої змінної здійснюється в блоці, то вона є локальною. На відміну від інших цей модифікатор повідомляє, що початкова об’ява здійснювалась в якомусь іншому файлі.
3. Зовнішні змінні функції Програма на C складається також з набору зовнішніх об'єктів — змінних або функцій. Прикметник «зовнішній» використовується як протиставлення «внутрішньому», який описує аргументи та змінні, визначені всередині функцій. Зовнішні змінні — означені поза межами будь-якої функції і, таким чином, потенційно доступні багатьом функціям. Самі функції завжди залишаються зовнішніми, оскільки C не дозволяє означати функції всередині інших функцій. Стандартно, зовнішні змінні і функції мають одну властивість, що полягає у тому, що всі звертання до них за тим самим ім'ям — навіть з функцій, компільованих окремо — посилаються на ту саму річ. Оскільки зовнішні змінні доступні глобально, вони служать альтернативою аргументам функцій, і можуть використовуватись для обміну даними між функціями. Будь-яка функція може звернутися до зовнішньої змінної, посилаючись на її ім'я, якщо це ім'я якимось чином оголошено. Зовнішні змінні також корисні через більшу область дії. Автоматичні змінні являються внутрішніми для функцій; вони з'являються при вході у функцію і зникають при виході з неї. Напротивагу, зовнішні змінні — постійні, а отже утримують значення від одного виклику функції до іншого. Таким чином, якщо дві функції мусять спільно використовувати якісь дані, але жодна з них не викликає іншої, часто найзручніше, щоб спільні дані зберігалися у зовнішніх змінних, замість передачі їх туди-сюди у вигляді аргументів.
4.Статичні змінні багато в чому подібні на глобальні змінні. Вони використовують модифікатор static. Якщо така змінна об`явлена глобально, то вона ініціалізується при запуску, а її область видимості співпадає з областю дії і поширюється від точки об`яви до кінця файлу, якщо ж статична змінна об`явлена в середині, то вона ініціалізується лише при першому вході у відповідну функцію або блок. Значення зберігаються від одного виклику ф-ції до іншого. Таким чином статичні змінні можна використовувати для збереження значення змінних на протязі роботи програми. Статичні змінні не можуть бути об`явлені в інших файлах, як зовнішні. Якщо статична змінна не ініціалізується, то їй присвоюється значення 0.
Змінні класу volatile. В тих випадках коли необхідно передбачити можливість модифікації змінної периферійним пристроєм або іншою програмою використовується тип volatile. У зв`язку з цим компілятор не намагається оптимізувати програму шляхом розміщення змінної в регістрах.
...
Volatile short sTest
...
або
...
Volatile cons tint sTest
… В другому випадку константа не може змінюватись в програмі, але може модифікуватися в залежності від зовнішніх факторів.
5. Простір іменВизначення ф-цій і змінних у файлах заголовків пов’язано з поняттям простору імен. До введення цього поняття всі обявлення ідентифікаторів і констант в заготовочному файлі розміщалися компілятором в глобальний простір імен, що приводить до великої кількості конфліктів, пов’язаних з використанням різними об’єктами однакових імен в одній програмі. Коли в програму включається заголовок нового стилю, його вміст розміщається не в глобальний простір а в простір std. Якщо в програмі треба визначити деякі ідентифікатори, які, як ви підозрюєте. Можуть перевизначати вже існуючі, просто треба завести свій власний новий простір імен. Для цього використовується ідентифікатор namespace.
namespaceімя простору імен
{ //обяви }
Таким чином після цього обяви в середині простору імен будуть знаходитись тільки в його області видимості. namespaceNewNameSpace
{ intx,y,z; voidsome_function(char smb); }
int main () { NewNameSpace::x=5; return 0; } Якщо :: використовуються часто, то можна використати інструкціюusing , яка має дві форми: 1) using namespace імя_простору ; 2)usingшмя_простору::ідентифікатор; 1)В першому випадку компілятору повідомляється, що в подальшому необхідно використовувати ідентифікатори з вказаного простору, поки не зустрінеться інша інструкція using. using Namespace; … x=0; y=z=4; some_function(‘A’); На практиці часто явно вказується явне позначення області: usingnamespacestd; 2) Друга форма дозволяє компілятору використовувати вказаний простір лише для конкретного ідентифікатора. using namespace std; using NEwNameSpace::z; так можна використовувати ідентифікатори стандартної бібліотеки с і цілочислову змінну z.
6. Вбудовані (inline-) функції. В результаті роботи компілятора кожна ф-ція представляється у вигляді машинного коду. Якщо в програмі ф-ція викликається декілька разів, то в місцях таких звернень чергуються коди виклику уже реалізованого екземпляру ф-ції. Однак виконання викликів вимагає затрати часу, тому, якщо тіло ф-ції не велике і звернення часті, на практиці можна вказати компілятору замість викликів ф-ції у відповідних місцях генерувати все її тіло з допомогою директиви inline. Цим збільшується продуктивність коду, хоча розмір програм зростає. Директива інлайн має бути включенна в програму перед першим викликом ф-ції, на приклад у прототипі.
nlineint sum(int, int) int main() { int a=2; int b= 6; int c=3; cout<<sum(a, b); cout<<sum(b, c); return 0; }
int sum(int x, int y) { returnx+y; }
7. Поняття рекурсивних функцій. Функція може викликати сама себе - це називається рекурсією. Рекурсія може бути прямою і непрямою. Коли функція викликає сама себе, то це пряма рекурсія, якщо функція викликає іншу функцію, яка потім викликає першу, то це непряма . Коли функція викликає сама себе, то виконується нова копія цієї функції, при цьому локальні змінні у 2 версії незалежні від локальних змінних першої версії і не можуть безпосередньо взаємно впливати одна на одну. Прикладом рекурсивної функції є обчислення чисел Фібоначчі: кожен наступний член починаючи з 3 дорівнює сумі двох попередніх. Для рекурсивних функцій важливою є умова зупинки рекурсій, наче, вона ніколи не закінчується. Для чисел Фібоначчі f>=3.
Алгоритм обчислення чисел Фібоначчі : 1)Вказуємо число n. 2)Виклик ф-ції Фібоначчі здійснюється використанням аргумента, якщо n<3, то ф-ція fib () викликає саму себе (рекурсивно), передаючи в якості аргументу значення n-1, а після цього повертає значення їх суми.
// Рекурсія чисел Фібоначчі # include <iostream.h> Int fib (int n); Int main () { int n, answer; cout <<”Enter number to find:”; cin >> n; cout <<”\n\n”; answer = fib(n); cout <<answer<<”is the”<<n<<”the Fibonachi number\n”; return 0; } Int fib(int n) { cout <<”Processing fib(“<<n<<”)….”; If (n<3) { cout <<”return 1\n”; return (1); } else { cout<<”call fib(”<<n-2<<”) and fib(“<<n-1<<”)\n”; return (fib n-2)+fib(n-1);}}