рефераты конспекты курсовые дипломные лекции шпоры

Реферат Курсовая Конспект

Синтаксис оголошення

Синтаксис оголошення - Конспект, раздел Образование, Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ Оголошення Складається З Чотирьох Частин: Необов'язкових Одного Або Декількох...

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

 

char *kings[] = { “Антигін”, “Сельовк”, “Птоломей”};

 

В даному прикладі базовим типом є char, часткою оголошення - *king[], а ініціалізатором є = {“Антигін”, “Сельовк”, “Птолемей”}.

В якості специфікаторів можуть виступати ключові слова, такі як virtual та extern; вони приводяться на початку оголошення і описують характеристики, не пов'язані з типом.

Тип повинен бути присутнім в будь-якому оголошенні.

Частка оголошення складається з ідентифікатора (імені) і, можливо, операторів оголошення (табл. 2.1):

 

Таблиця 2.1

Оператори оголошень

Оператор Призначення Місце в оголошенні
* вказівник префікс
*const константний вказівник префікс
& посилання префікс
[] масив суфікс
() функція суфікс

 

Приклади оголошень:

 

const s = 7; // помилка - не вказаний тип

gt(int а, int b){return (a>b) ? а: b;} // помилка - не заданий тип значення,

// яке повертає функція

unsigned ui; // правильно – unsigned є

// скороченим записом unsigned int

long li; // правильно – long є

// скороченим записом long int

2.1.2 Оголошення декількох імен

У одній інструкції оголошення дозволяється оголошувати декілька імен. В такому випадку оголошення повинне містити список об'явників, розділених комами. Наприклад:

 

int x, у; // рівносильно оголошенню двох цілих змінних:

// int x;

// int у;

int *p, у; // оператор оголошення * застосовується тільки до

// найближчого імені; тому це оголошення рівносильно:

// int* p;

// int у; (не int *y; )

int v[10], *q; // int v[10], int *q;

 

Останні два варіанти оголошень використовувати не рекомендується із – за їх нетривіального трактування оголошуваних через кому імен.

2.1.3 Ідентифікатори

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

Ідентифікатор є послідовністю символів довільної довжини, що містить букви, цифри і символи підкреслення та обов'язково повинна починатися з букви або символу підкреслення. Компілятор Microsoft Visual C++ розпізнає тільки перші 2048 символів ідентифікатора.

У мові C++ враховується регістр букв. Іншими словами, компілятор сприймає великі та малі літери як різні символи. Наприклад, імена fileName і FileName є різними ідентифікаторами. Тобто можна оголошувати ідентифікатори, які читаються однаково, але розрізняються регістром однієї або декількох букв.

Використання і великих і малих літер в ідентифікаторах дозволяє зробити програму більш читабельною. Наприклад, можна оголосити ім'я типу користувача, що починається із великої літери, а змінну цього ж типу іменувати тим же ідентифікатором, замінивши в ньому першу букву на малу. Нижче показані приклади ідентифікаторів:

 

i2

iTotal

fRange

szFirst_name

imax

iMAX

 

Імена, приведені нижче, ідентифікаторами не є:

 

1st_year

#security

Not_Done!

 

Послідовність символів будь-якого ідентифікатора з урахуванням регістра не повинна співпадати ні з одним з ключових слів мови C++, а також ні з одним з ключових слів, доданих фірмою Microsoft при реалізації системи програмування Visual C++ .NET (всі вони починаються з двох символів підкреслення). Перелік всіх ключових слів системи програмування Microsoft Visual C++ .NET приведений в табл. 2.2.

 

Таблиця 2.2

Ключові слова системи програмування Microsoft Visual C++ .NET

__abstract 2 __alignof __asm __assume
__based __box 2 __cdecl __declspec
__delegate2 __event __except __fastcall
__finally __forceinline __gc 2 __hook 3
__identifier __if_exists __if_not_exists __inline
__int8 __int16 __int32 __int64
__interface __leave __m64 __m128
__m128d __m128i __multiple_inheritance __nogc 2
__noop __pin 2 __property 2 __raise
__sealed 2 __single_inheritance __stdcall __super
__try_cast 2 __try/__except, __try/__finally __unhook 3 __uuidof
__value 2 __virtual_inheritance __w64 bool
break case catch char
class const const_cast continue
default delete deprecated 1 dllexport 1
dllimport 1 do double dynamic_cast
else enum explicit extern
false float for friend
goto if inline int
long mutable naked 1 namespace
new noinline 1 noreturn 1 nothrow 1
novtable 1 operator private property 1
protected public register reinterpret_cast
return selectany 1 short signed
sizeof static static_cast struct
switch template this thread 1
throw true try typedef
typeid typename union unsigned
using uuid 1 virtual void
volatile __wchar_t, wchar_t while  

 

Виноски:

1. Розширені атрибути |ключового слова __declspec.

2. Застосовується тільки до керованих розширень мови Microsoft Visual C++ .NET .

3. Вбудовані функції управління подіями мови Microsoft Visual C++ .NET.

2.1.4 Область видимості імен

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

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

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

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

У великих програмах не уникнути перевизначення імен. На жаль, легко можна продивитися таке перевизначення. Помилки, що виникають через це, знайти непросто, можливо тому, що вони достатньо рідкісні. Отже, перевизначення імен слід звести до мінімуму. Якщо глобальні змінні або локальні змінні у великій функції ідентифікуються такими іменами, як i або x, то це з великою вірогідністю викличе неприємності.

Є можливість за допомогою оператора розширення області видимості (::) звернутися до прихованого глобального імені, наприклад:

 

int x;

void f2()

{

int x = 1; // приховує глобальне x

::x = 2; // присвоювання глобальному x

}

 

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

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

2.1.5 Час життя об'єктів

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

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

 

#include <iostream>

using namespace std;

int а = 1;

void f()

{

int b = 1; // ініціалізується при кожному виклику f()

static int s = а; // ініціалізується тільки один раз

cout << " а = " << a++

<< " b = " << b++

<< " s = " << c++ << 'n';

}

 

int main()

{

while (а < 4) f();

}

 

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

 

а = 1 b = 1 s = 1

а = 2 b = 1 s = 2

а = 3 b = 1 s = 3

 

Глобальна змінна або локальна змінна зі словом static, яка явно не була ініціалізована, ініціалізується неявно нульовим значенням.

Використовуючи оператори new і delete, програміст може створювати об'єкти, часом життя яких він управляє сам.

2.1.6 Ініціалізація об'єктів

Якщо в оголошенні об'єкту вказаний ініціалізатор, він визначає початкове значення цього об'єкту.

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

Приклади глобальних оголошень:

 

int а; // означає int а = 0;

double d; // означає double d = 0.0;

 

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

2.1.7 Специфікатори сталості змінних

У мові C++ використовуються два специфікатори сталості змінних: const і volatile. Вони потрібні для позначення змінних, значення яких не може мінятися (const), і змінних, значення яких можуть мінятися у будь-який момент часу (volatile).

2.1.7.1 Специфікатор const

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

Наприклад, якщо в програмі обчислюється довжина кола або площа круга, то використовується число π (3.141592), точність завдання якого може змінюватися в ході розробки програми. У бухгалтерських програмах часто використовується величина ПДВ, значення якої час від часу може мінятися незалежно від програми. Тому зручно описати ці величини як змінні, значення яких постійні в процесі виконання програми.

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

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

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

2.1.7.2 Специфікатор volatile

Ключове слово volatile вказує на те, що даний об'єкт у будь-яку мить може бути змінений в результаті зовнішніх дій, не контрольованих програмою. Наприклад, наступний рядок оголошує змінну event_time, значення якої може змінюватися системним таймером без відома програми:

 

volatile int event_time;

 

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

Специфікатор volatile забороняє компілятору розміщувати змінну в регістрах процесора, що важливо, якщо змінна одночасно використовується декількома потоками виконання програми (threads). Специфікатор volatile якраз і застосовується при оголошенні об'єктів даних, що спільно використовуються різними процесами або потоками в багатозадачному середовищі.

Допускається одночасне використання ключових слів const і volatile при оголошенні об'єктів. Так, в наступному рядку створюється оновлювана ззовні змінна, значення якої не може бути модифіковано самою програмою:

 

const volatile int const_event_time;

2.1.8 Розміщення іменованих об'єктів в пам'яті

Існують чотири специфікатори, які управляють розміщенням програмних об'єктів в пам'яті і впливають на область видимості та час життя об'єктів:

- register,

- auto,

- static,

- extern.

У мові C++ передбачені чотири основні| види пам'яті, в яких можуть розміщуватися програмні об'єкти:

- регістрова пам'ять,

- локальна (стекова) пам'ять,

- статична пам'ять,

- динамічна (вільна) пам'ять.

Управління розміщенням об'єктів у перших трьох видах пам'яті здійснюється за допомогою специфікаторів і буде розглянуто в даному підрозділі.

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

2.1.8.1 Регістрова пам'ять. Специфікатор register

Специфікатор register наказує компілятору спробувати розмістити оголошувану змінну в регістрах процесора. Якщо це компілятору не вдається, змінна поводиться як звичайна локальна змінна з специфікатором auto.

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

Наприклад:

 

int main()

{

register double sum;

...

}

 

Специфікатор register не можна застосовувати до глобальних змінних програми.

Компілятор Visual C++ .NET ігнорує специфікатор register.

2.1.8.2 Локальна пам'ять. Специфікатор auto

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

Локальну пам'ять називають стековою, оскільки аргументи функцій і локальні змінні компілятором розміщуються в стековій пам'яті, яка виділяється операційною системою кожному паралельно працюючому потоку (thread).

Специфікатор auto для локальних змінних використовується за умовчанням, тому його практично ніколи не вказують.

Наприклад:

 

int main()

{

auto int а = 1; // те ж, що int а = 1;

...

}

2.1.8.3 Статична пам'ять. Специфікатор static

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

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

Статичні об'єкти можуть викликати проблеми при їх сумісному використанні в паралельно функціонуючих потоках, оскільки вони розміщуються в пам'яті, загальній для всіх потоків (threads) одного процесу (process). Для правильного, передбачуваного поводження таких об'єктів потрібно використовувати програмні механізми синхронізації.

При оголошенні статичних об'єктів використовується специфікатор static. Якщо такий об'єкт оголошений глобально, то він ініціалізується при запуску програми, а його область видимості тягнеться від точки оголошення до кінця файлу.

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

Як було сказано раніше, якщо ініціалізатор для статичного об'єкту не заданий, то об'єкту присвоюється нульове значення відповідного типу.

Статичні об'єкти не можуть бути оголошені в інших файлах як зовнішні.

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

 

int Count(void)

{

static int counter = 0;

return counter++;

}

 

int main()

{

int count = 0;

double result = 0.0;

while (count < 30)

result += 1/ (count = Count());

cout << result;

}

 

Тут в головній функції в циклі 30 разів буде викликана функціяCount(), яка, завдяки статичній змінній counter, послідовно поверне ряд чисел: 1, 2 ., 30. В результаті, в циклі буде обчислена сума 1/1 + 1/2 + . + 1/30, яка після закінчення циклу буде виведена на екран.

2.1.8.4 Специфікатор extern

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

На відміну від вже розглянутих, специфікатор extern повідомляє компілятору, що фактичне оголошення змінної з цим ім'ям і виділення пам'яті для змінної проводиться в якомусь іншому файлі програми.

Розглянемо приклад з поясненнями, приведеними в коментарях:

 

// файл other.cpp

#include <iostream>

using namespace std;

int i = 1;

void other()

{

static int *pi = &i; // адреса глобальної змінної i

// поміщається в статичний вказівник pi

int i = 16; // глобальна змінна i закрита

// локальною змінною i із значенням 16

cout << i << ‘n’; // вивід значення локальної i (=16)

cout << *pi << ‘n’; // вивід значення глобальної змінної i (=1),

// використовуючи адресу останньої

}

 

// файл main.cpp

int main()

{

extern int i; // посилання на i, оголошену в модулі other.cpp

register int b = 0; // b знаходитиметься в регістрі

cout << i << ‘n’; // вивід зовнішньої i (=1), яка знаходиться в other.cpp

cout << b << ‘n’; // вивід локальної b (=0)

other();

return;

}

 

Специфікатор extern може також використовуватися при оголошенні функцій. При цьому вслід за специфікатором може розміщуватися рядок, який задає мову, за правилом якої здійснюється передача аргументів і виклик функції. Для Microsoft Visual C++ такими рядками можуть бути "C" та "C++". У всіх стандартних бібліотеках зі специфікатором extern використовується рядок "C", що дає можливість в програмах на C++ використовувати всі розроблені раніше бібліотечні функції.

Наприклад:

 

// оголошення функції, яка викликається за правилами мови C

extern "C" int printf(const char *fmt, ... );

// оголошення двох функцій, що викликаються за правилами мови C

extern "C"

{

char ShowChar(char ch);

char GetChar(void);

}

2.1.9 Динамічна пам'ять

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

Такі об'єкти створюються за допомогою оператора new і знищуються за допомогою оператора delete. Пам'ять під об'єкти, контрольованими цими операторами, виділяється з спеціального виду пам’яті програми на мові С++ - динамічної пам'яті. Цей вид пам'яті в літературі іноді називають “вільною пам'яттю” або просто “купою”.

2.1.9.1 Оператори new і delete

Основна форма використання операторів new і delete для роботи з простими об'єктами наступна:

 

pointer = new type (початкове значення);

delete pointer;

 

Оператор new виділяє пам'ять під об'єкт, розмір якого визначається його типом type, що задається праворуч від оператора. Адреса виділеної в динамічній пам'яті ділянки заноситься у вказівник pointer, який задається зліва від оператора присвоювання. Якщо вказано початкове значення в дужках після типу об'єкту, об'єкт ініціалізувався цим значенням.

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

Оператор delete звільняє ділянку пам'яті, зайняту об'єктом, початкова адреса якого міститься у вказівнику pointer. Дозволяється також застосовувати цей оператор до вказівника, що містить нульове значення.

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

Наприклад:

 

#include <iostream>

using namespace std;

int main()

{

int *p;

p = new int; // виділення пам'яті під цілу змінну

if(!p)

{

cout << "Помилка виділення пам'яті n";

return 1;

}

*p = 1000;

cout << "Ціле, на яке вказує p: " << *p << "n";

delete p; // звільнення пам'яті

return 0;

}

 

В системі програмування Microsoft Visual C++ .NET для програм, написаних на некерованому C++, не передбачається наявність спеціальної програми збірки сміття, тобто програми автоматичного знищення з динамічної пам'яті об'єктів, посилання на які в програмах відсутні. Отже, об'єкти, створені оператором new, обов'язково повинні видалятися з динамічної пам’яті оператором delete перед обнулінням вказівників, що посилаються на динамічні об'єкти.

2.1.9.2 Оператори new[] і delete[]

За допомогою операторів new[] і delete[] в динамічній пам'яті можна створювати і знищувати масиви об'єктів. Форма використання операторів new[] і delete[] для роботи з масивами об'єктів наступна:

 

pointer = new type[size];

delete[] pointer;

 

Оператор new[] виділяє пам'ять під масив об'єктів із заданою кількістю елементів size і повертає вказівник на перший елемент масиву. Оператор delete[] звільняє ділянку пам'яті, зайняту масивом об'єктів.

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

 

#include <iostream>

using namespace std;

int main()

{

int *p;

p = new int[5]; // виділення пам'яті для 5-ти цілих елементів

if(!p) // потрібно переконатися, що пам'ять виділена

{

cout << "Помилка виділення памяті n";

return 1;

}

int i;

for(i = 0; i<5; i++) p[i]= i;

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

{

cout << "Це ціле, на яке вказує p[" << i << "]: ";

cout << p[i]<< "n";

}

delete[] p; // звільнення пам'яті масиву

return 0;

}

 

Програма виведе на екран наступне:

 

Це ціле, на яке вказує p[0]: 0

Це ціле, на яке вказує p[1]: 1

Це ціле, на яке вказує p[2]: 2

Це ціле, на яке вказує p[3]: 3

Це ціле, на яке вказує p[4]: 4

2.1.9.3 Обробка помилок виділення пам'яті

Якщо оператор new не може виділити необхідного об'єму пам'яті, генерується виключення bad_alloc. Наприклад:

 

void f()

{

try

{

for(;;) new char[10000];

}

catch (bad_alloc)

{

cerr << “Немає вільної пам'яті!n”;

}

}

 

Оскільки цикл безкінечний, коли вся динамічна пам'ять буде витрачена, генерується виключення bad_alloc і робота циклу припиняється. Оскільки цикл знаходиться під контролем блоку try, управління буде передано блоку – перехоплювачу catch, який виведе повідомлення про брак пам'яті. Детальний опис роботи механізму виключень міститься в четвертому розділі даної книги.

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

 

#include <iostream>

using namespace std;

void out_of_store() // функція– обробник відсутності пам'яті

{

cerr << “Неприпустиме завершення оператора new: ”

<< “немає динамічної пам'яті.n”;

}

 

int main()

{

set_new_handler(out_of_store); // завдання функції – обробника

// відсутності пам'яті

for(; ;) new char[10000];

cout << “Справа зроблена!n”;

}

 

Повідомлення «Справа зроблена!» ніколи не буде виведено, оскільки при браку пам'яті управління буде передано функції out_of_store(), яка виведе повідомлення «Неприпустиме завершення оператора new: немає динамічної пам'яті».

2.1.9.4 Автоматичне збирання сміття

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

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

 

void f()

{

int *p = new int;

p = 0;

char *q = new char;

}

 

В програмі інструкція присвоювання p = 0; залишає без посилання цілу змінну, розміщену в динамічній пам'яті інструкцією int *p = new int;. Тому, за наявності в системі автоматичного збирання сміття, на її місце інструкцією char *q = new char; може бути поміщена нова змінна типу char. В цьому випадку вказівник q міститиме ту ж адресу, що і р.

Автоматичне збирання сміття стає все більш популярним. В даний час автоматичне збирання сміття використовується в системі програмування Microsoft Visual C++ .NET при роботі програм, написаних на керованій мові C++, і є наріжним каменем цього діалекту C++.

З самого початку мови Visual Basic і C#, а також системи програмування для них проектувалися з розрахунку на наявність в системі автоматичного збирання сміття. Тому ручне управління розподілом пам'яті в програмах на цих мовах неможливе і, як наслідок, в цих мовах відсутні вказівники і пряма робота з ними.

2.1.9.5 Фрагментація пам'яті

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

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

У системах з автоматичним збиранням сміття проблема фрагментації пам'яті вирішується застосуванням одного з двох видів збирачів сміття:

- копіюючого збирача, який копіює «живі» об'єкти і таким чином стискає пам'ять, усуваючи фрагментацію;

- консервативного збирача, який розміщує об'єкти так, щоб мінімізувати фрагментацію пам'яті.

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

Проте в системі програмування Microsoft .NET, яка забезпечує розробку та функціонування програм, написаних на мовах C++, C# та Visual Basic, використовуються алгоритми саме копіюючого збирача сміття.

2.2 Вказівники

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

При оголошенні вказівника на об'єкт даних перед його ім'ям ставиться символ *, що є ознакою непрямої адресації, а перед символом * задається тип побічно адресованого об'єкту:

 

type *pointer;

 

Тут через pointer позначено ім'я змінної – вказівника, яке підкоряється тим же правилам іменування, що і для звичайних змінних.

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

 

type (*pointer)(список аргументів з їх типами);

 

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

Наприклад:

 

char *p; // вказівник на символ

int *q; // вказівник на ціле

int *ip = &ia[2]; // вказівнику на ціле присвоєна адреса третього

// елемента масиву ia

int **t; // вказівник на вказівник на цілу змінну

char *pa[7]; // масив з 7 вказівників на символи

double (*fp)(double); // вказівник на функцію, аргументом

// якої є число типу double, та

// яка повертає результат типу double

 

Перед використанням в програмі вказівник повинен містити адресу потрібного об'єкту. Перед присвоюванням вказівнику адреса змінної, елемента масиву, функції або іншого об'єкту може бути одержана за допомогою оператора взяття адреси &. Наприклад:

 

p = &v[3]; //вказівник містить адресу 4-го елемента масиву v

q = &inch; // вказівник містить адресу змінної inch

fp = &sqrt; // вказівник містить адресу функції sqrt()

 

Над вказівниками можуть виконуватися арифметичні дії, що мають сенс для вказівників, наприклад:

 

p = p + 2; // вказівник містить адресу 6-го елементу масиву v

 

Для доступу до значення об'єкту, що адресується вказівником, використовується оператор розіменування вказівника *. Наприклад:

 

int i = *q; // змінна i містить значення, яке

// містилося в змінній inch

*p = 'c'; // 6-му| елементу масиву v присвоєний

// код символу 'c'

double sq = (*fp)(4.0); // змінна sq містить 2.0 - результат

// виконання функції sqrt(4.0)

 

Ім'я масиву можна використовувати як вказівник на його перший елемент. Наприклад:

 

int v[] = {1, 2, 3, 4};

int *p1 = v; // p1 містить вказівник на 1-й елемент масиву v

int *p2 = &v[0]; // p2 містить вказівник на 1-й елемент масиву v

int *p3 = &v[4]; // p3 містить вказівник на елемент, наступний

// за останнім елементом масиву v

 

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

За допомогою вказівників можна створювати надзвичайно ефективні алгоритми й розробляти дійсно професійні програми. Але при цьому слід враховувати, що складність програм, в яких використовуються вказівники, значно зростає. Саме тому явна робота з вказівниками відсутня в більшості мов програмування, включаючи мови швидкої розробки програм, такі як Visual Basic та C#.

2.2.1 Вказівники та константи

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

Наприклад:

 

char *p = "asdf"; // звичайний вказівник

const char *pc ="ghjk" ; // вказівник на константу

pc[3]= 'a'; // помилка, pc указує на константу

pc = p; // нормально

 

Щоб оголосити як константу сам вказівник, а не вказуваний ним об'єкт, потрібно використовувати оператор оголошення *const, а не просто const. Крім того, оператор *const потрібно ставити після типу об'єкту.

Наприклад:

 

char *p = "asdf"; // звичайний вказівник

char *const сp ="ghjk" ; // вказівник - константа

сp[3]= 'a'; // нормально

сp = p; // помилка, вказівник сp - константа

 

Щоб зробити константами і вказівник, і об'єкт, треба обидва оголосити як const, наприклад:

 

char *p = "asdf"; // звичайний вказівник

const char *const cpc = "asdf"; // вказівник-константа і об'єкт - константа

cpc[3]= 'a'; // помилка

cpc = p; // помилка

 

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

Оголосивши для функції аргумент - вказівник як const, функціяне зможе змінити об'єкт, що адресується вказівником. Наприклад:

 

char* strcpy(char* p, const char* q); // функціяне може змінювати *q

 

Вказівнику на константу можна присвоїти адресу змінної, оскільки це не принесе шкоди. Проте адресу константи не можна присвоїти вказівнику без специфікатора const, інакше можна змінити значення об'єкту. Наприклад:

 

int а = 1;

const int c = 2;

const int *p1 = &c; // нормально

const int *p2 = &a; // нормально

int *p3 = &c; // помилка, оскільки можлива наступна інструкція,

*p3 = 7; // що міняє значення змінної c

2.2.2 Вказівник на void

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

 

void* pv; // вказівник на об'єкт довільного типу

int g(void); // функціябез аргументів, рівнозначно int g();

void f(); // f() не повертає значення

 

 

Вказівник void* може указувати на об'єкт будь-якого типу, якщо цей об'єкт оголошений без специфікаторів const або volatile.

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

Оскільки об'єктів типу void не існує, вказівники void* не можна розіменовувати.

Тип void* приписується аргументам функцій, які не знають або не повинні знати істинного типу цих аргументів. Тип void* мають також| об'єкти без типу, що повертаються функціями. Для використання таких об'єктів потрібно виконати явну операцію приведення типів. Такі функції зазвичай знаходяться на найнижчих рівнях програмної системи, які управляють апаратними ресурсами. Наведемо приклад:

 

void* malloc(unsigned size); // бібліотечна функціямови C –

// аналог оператора new мови C++

void free(void*); // бібліотечна функція мовиC –

// аналог оператора delete мови C++

void f() // розподіл пам'яті в стилі мови C

{

int* pi = (int*)malloc(10*sizeof(int));

char* pc = (char*)malloc(10);

//...

free(pi);

free(pc);

}

 

У програмі синтаксис: (тип) вираз - використовується для завдання операції приведення виразу до іншого типу. Отже, в приведеній вище програмі перед присвоюванням вказівнику pi тип void*, який повертається в першому виклику malloc(), приводиться до типу int.

– Конец работы –

Эта тема принадлежит разделу:

Конспект лекцій СИСТЕМНЕ ПРОГРАМУВАННЯ

Конспект лекцій... по дисциплiні СИСТЕМНЕ ПРОГРАМУВАННЯ...

Если Вам нужно дополнительный материал на эту тему, или Вы не нашли то, что искали, рекомендуем воспользоваться поиском по нашей базе работ: Синтаксис оголошення

Что будем делать с полученным материалом:

Если этот материал оказался полезным ля Вас, Вы можете сохранить его на свою страничку в социальных сетях:

Все темы данного раздела:

РОБОТА З ДАНИМИ
Якість організації і представлення даних, що обробляються програмами, має не менше значення, ніж хороше розбиття програм на функції, обробляючі ці дані, та реалізація алгоритмів цих функцій. Більш

Оголошення
В мові С++ кожне ім'я (ідентифікатор) слід оголосити перш, ніж воно буде використовуватися в програмі. Це означає, що потрібно вказати його тип, щоб компілятор знав, до якого виду об'єктів має відн

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

Типи даних користувача
Описані раніше вбудовані типи даних мови C++ можуть бути доповнені новими типами даних, визначеними самим користувачем. Типи даних користувача діляться на три групи: переліки, структури й об'єднанн

Структури мови C
Масив є сукупністю елементів одного типу, а структура є сукупністю елементів різних типів. Оголошення структури має наступний формат:   struct struct_name {

Оголошення typedef
Оголошення, що починається з ключового слова typedef, вводить нове ім'я (синонім) для типу, а не оголошує нову змінну цього типу. Формат оголошення typedef наступний:   typed

Масиви як аргументи
Якщо в якості аргументу функції задається масив, то передається вказівник на його перший елемент. Наприклад:   int strlen(const char*); void f() {

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

Макрос WINAPI
Оскільки компілятор Visual C++ більше не підтримує ключових слів __pascal, __fortran та __syscall, їх дію рекомендується емулювати за допомогою описаних вище ключових слів __cdecl, __stdcall або __

ПРОГРАМУВАННЯ ВИКЛЮЧЕНЬ
Виключною ситуацією, або виключенням (exception), називається переривання нормального потоку виконання програми у відповідь на непередбачену або аварійну подію. Події, що приводять до викл

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

Обробка виключень Win32
Існує декілька способів обробки виключень Win32. Найбільш простий з них полягає в використанні блоку catch з трьома крапками:   try { // … // генер

МОДУЛЬНЕ ПРОГРАМУВАННЯ
Набір зв'язаних процедур разом з даними, які вони обробляють, називається модулем. Модуль – програмний фрагмент, який є будівельним блоком для побудови великих програм. Як правило, модуль складаєть

Директиви препроцесора
Існує 14 стандартних директив препроцесора:   #include #define #undef #if #ifdef #ifndef #elif #else #

Макроси
Директива #define для оголошення макросів має дві синтаксичні форми:   #define ідентифікатор макроозначення #define ідентифікатор (список параметрів) макроозначення

Конструктори
Раніше наведене оголошення класу CFrame дозволяє ініціалізувати змінні-члени об'єктів класу шляхом виклику програмою користувача функції-члена класу SetCoord(). В якості іншого, стандартно

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

Деструктори
Відповідно до принципів об'єктно-орієнтованого програмування| в класі можна оголосити спеціальну функцію-член|, звану деструктором. Деструктор автоматично викликається кожного разу при зни

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

Переваги спадкування
Як випливає з приведених вище простих прикладів, спадкування дозволяє повторно використовувати створені раніше фрагменти програм і структури даних. Це дозволяє уникнути непотрібного дублювання прог

Конструктори приведення
Конструктор приведення класу — це конструктор з єдиним аргументом, тип якого відрізняється від типу класу. Такий конструктор звичайно ініціалізує новий об'єкт, використовуючи літерали, або дані змі

УЗАГАЛЬНЕНЕ ПРОГРАМУВАННЯ
Узагальнене програмування з використанням шаблонів мови C++ полегшує генерацію сімейств функцій або класів, що оперують множинами даних різних типів. При цьому не виникає необхідності створювати ок

Клас string
У мові C++ для представлення і обробки рядків є дві можливості. По-перше, можна використовувати символьний масив, що закінчується нулем і є рядком в стилі мови C, як це й робилося практичн

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

Потоки вводу - виводу
Як вже мовилося раніше, якщо програма на мові C починає виконуватися, автоматично відкриваються три потоки: stdin, stdout і stderr.Щось схоже має місце при завантаженні програми, написаної на мові

Класи потокового вводу - виводу
У C++ ввід - вивід забезпечується підключенням до програми заголовного файлу <iostream>. Саме в цьому файлі оголошені класи, що підтримують операції вводу – виводу. Система вводу – в

Прапори формату
Кожен потік вводу - виводу пов'язаний з набором прапорів формату (format flags), які управляють способом форматування інформації і є бітовими масками. Ці маски оголошені в класі ios як дані перелік

Закриття файлу
Для закриття файлу використовується функція-член| close(). Наприклад, щоб закрити файл, пов'язаний з потоком mystream, необхідна наступна інструкція:   mystream.close ();

Контроль стану вводу - виводу
В системі вводу - виводу C++ підтримується інформація про стан після кожної операції вводу - виводу. Поточний стан потоку вводу - виводу, який зберігається в об'єкті типу iostate, є переліком, визн

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

Мова програмування Visual Basic .NET
Хоча мови програмування керований C++, С# та Visual Basic .NET (VB) семантично мають практично однакові можливості, оскільки всі вони базуються на загальній специфікації мов програмування .NET (CLS

Цикл For
В мові С++ цикл for має дещо інший синтаксис, ніж цикл For в мові VB, але концепція залишається тою ж самою, якщо не зважати на те, що операція, яка виконується в кінці кожної ітерації, має бути вк

Хотите получать на электронную почту самые свежие новости?
Education Insider Sample
Подпишитесь на Нашу рассылку
Наша политика приватности обеспечивает 100% безопасность и анонимность Ваших E-Mail
Реклама
Соответствующий теме материал
  • Похожее
  • Популярное
  • Облако тегов
  • Здесь
  • Временно
  • Пусто
Теги