Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Sizeof value 1 страница




Else

змінна = вираз2;

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

Оператор "?" називається тернарним|, оскільки він працює з трьома операндами. Ось його загальний формат запису:

Вираз1? Вираз2: Вираз3;

Всі елементи тут є виразами. Звернемо Вашу увагу на використання і розташування двокрапки.

Значення?-виразу визначається таким чином. Обчислюється Вираз1. Якщо він виявляється істинним, то обчислюється Вираз2, і результат його обчислення стає значенням всього?-виразу. Якщо результат обчислення елемента Вираз1 виявляється помилковим, то значення всього?-виразу стає результатом обчислення елемента ВиразЗ. Розглянемо такий приклад:

while (something) {

х = count > 0? 0: 1;

//...

}

У цьому записі змінній х присвоюватиметься значення 0 доти, доки значення змінної count не стане меншим або дорівнюватиме нулю. Аналогічний програмний код (але з використанням if - else -настанові) виглядав би так:

while (something) {

if (count > 0) х = 0;

else x = 1;

//...

}

А ось ще один приклад практичного застосування оператора "?". Наведений нижче код програми ділить два числа, але не допускає ділення на нуль.

Код програми 9.12. Демонстрація механізму використання оператора "?" для запобігання ділення на нуль

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

int div_zero();

 

int main ()

{

int izm, jzm, result;

 

cout << "Введіть ділене і дільник: ";

cin >> izm >> jzm;

 

// Ця настанова не допустить виникнення помилки ділення на нуль.

result = jzm? izm/jzm: div_zero();

 

cout << "Результат: " << result;

 

getch (); return 0;

}

 

int div_zero() {

cout << "Не можна ділити на нуль.\n";

 

getch (); return 0;

}

У цій програмі, якщо значення змінної jzm не дорівнює нулю, то виконується ділення значення змінної izm на значення змінної jzm, а результат присвоюється змінній result. Інакше викликається обробник помилки ділення на нуль div_zero(), і змінній result присвоюється нульове значення.

9.4.4. Складені оператори присвоєння

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

х = х + 10;

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

х += 10;

Пара операторів += слугує вказанням компіляторові присвоїти змінній х суму поточного значення змінної х і числа 10. Цей приклад є ілюстрацією того, що складені оператори присвоєння спрощують програмування певних настанов присвоєння. Окрім того, вони дають змогу компіляторові згенерувати ефективніший код.

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

змінна = змінна ор| вираз;

загальна форма запису їх складених версій має такий вигляд:

змінна ор| = вираз;

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

А ось ще один приклад. Настанова

х = х – 100;

аналогічна такій:

х –= 100;

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

9.4.5. Оператор "кома"

Не менш цікавим, ніж описані вище оператори, є такий оператор мови програмування C++, як "кома". Ви вже бачили декілька прикладів його використання в циклі for, де за допомогою "коми" було організовано ініціалізацію відразу декількох змінних. Але оператор "кома" також може складати частину виразу. Його призначення у цьому випадку – зв'язати певним чином декілька виразів. Значення перерахунку виразів, розділених між собою комами, визначається у цьому випадку значенням крайнього справа виразу. Значення інших виразів відкидаються. Отже, значення виразу справа стає значенням всього виразу-списку. Наприклад, у процесі виконання цієї настанови

var = (count=19, incr=10, count+1);

змінній count спочатку присвоюється число 19, змінною incr – число 10, а потім до значення змінної count додається одиниця, після чого змінній var присвоюється значення крайнього справа виразу, тобто count+1, яке дорівнює 20. Круглі дужки тут обов'язкові, оскільки оператор "кома" має нижчий пріоритет, ніж оператор присвоєння.

Щоб зрозуміти призначення оператора "кома", спробуємо виконати таку програму:

Код програми 9.13. Демонстрація механізму використання оператора "кома"

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main ()

{

int izm, jzm;

 

jzm = 10;

izm = (jzm++, jzm+100, 999+jzm);

cout << izm;

 

getch (); return 0;

}

Ця програма виводить на екран число 1010. І ось чому: спочатку змінній jzm присвоюється число 10, потім змінна jzm інкрементується до 11. Після цього обчислюється вираз jzm+100, який ніде не застосовується. Нарешті, виконується додавання значення змінної jzm (вона, як і раніше, дорівнює 11) з числом 999, що в результаті дає число 1010.

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

9.4.6. Декілька присвоєнь "в одному"

Мова програмування C++ дає змогу застосувати дуже зручний метод одночасного присвоєння багатьом змінним одного і того ж значення. Йдеться про об'єднання відразу декількох присвоєнь в одній настанові. Наприклад, у процесі виконання цієї настанови змінним count, incr і index буде присвоєне число 10:

count = incr = index = 10;

Цей формат присвоєння декільком змінним загального значення можна часто натрапити в професійно написаних програмах.

9.4.7. Використання ключового слова sizeof

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

sizeof (type)

Оператор sizeof під час компілювання програми отримує розмір типу або значення.

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

Щоб зрозуміти, як працює оператор sizeof, випробуйте таку коротку програму. Для багатьох 32-розрядних середовищ вона повинна відобразити значення 1, 4, 4 і 8.

Код програми 9.14. Демонстрація механізму використання оператора sizeof

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main ()

{

char ch;

int izm;

cout << sizeof ch << "\n"; // розмір типу char

cout << sizeof izm << "\n"; // розмір типу int

cout << sizeof (float) << "\n"; // розмір типу float

cout << sizeof (double) << "\n"; // розмір типу double

 

getch (); return 0;

}

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

Оператора sizeof можна застосувати до будь-якого типу даних. Наприклад, у разі застосування його до масиву він повертає кількість байтів, які займає масив. Розглянемо такий фрагмент коду програми:

int tMas[4];

cout << sizeof tMas; // Буде виведене число 16.

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

Оператор sizeof в основному використовується під час написання коду програми, який залежить від розміру С++-типів даних. Пам'ятайте: оскільки розміри типів даних у мові програмування C++ визначаються конкретною реалізацією, не варто покладатися на розміри типів, визначених в реалізації, у якій Ви працюєте в цей момент.

Рис. 9.1. Використання пам'яті у мові програмування C++

9.5. С++-система динамічного розподілу пам'яті

Для С++-програми існує два основні способи зберігання інформації в основній пам'яті комп'ютера. Перший полягає у використанні змінних. Область пам'яті, що надається змінним, закріплюється за ними під час компілювання і не може бути змінена у процесі виконання програми. Другий спосіб полягає у використанні С++-системи динамічного розподілу пам'яті. У цьому випадку пам'ять для даних виділяється в міру потреби з розділу вільної пам'яті, який розташований між Вашою програмою (і її постійною областю зберігання) та стеком. Цей розділ на­зи­вається " купою" (heap). Розташування програми в пам'яті схематично показано на рис. 9.1.

Система динамічного розподілу пам'яті – це засіб отримання програмою деякої області пам'яті під час її виконання.

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

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

9.5.1. Оператори динамічного розподілу пам'яті

Мова C++ містить два оператори – new і delete, які виконують функції з виділення та звільнення пам'яті. Їх загальний формат має такий вигляд:

змінна-покажчик = new тип_змінної;

delete змінна-покажчик;

У цьому записі елемент змінна-покажчик є покажчиком на значення, тип якого задано елементом тип_змінної. Оператор new виділяє область пам'яті, достатню для зберігання значення заданого типу, і повертає покажчик на цю область пам'яті. За допомогою оператора new можна виділити пам'ять для значень будь-якого допустимого типу.

Оператор new дає змогу динамічно виділити область пам'яті.

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

Оператор delete звільняє раніше виділену динамічну пам'ять.

Оскільки об'єм "купи" є скінченим, то вона може коли-небудь вичерпатися. Якщо для задоволення чергового запиту на виділення пам'яті не існує достатньо вільної пам'яті, то оператор new зазнає фіаско, і згенерує виняток. Виняток – це помилка спеціального типу, яка виникає у процесі виконання програми (у мові програмування C++ передбачена цілу підсистему, призначену для оброблення таких помилок (див. розд. 18)). У загальному випадку Ваша програма повинна обробити подібний виняток і за змогою виконати дію, відповідну конкретній ситуації. Якщо цей виняток не буде оброблено Вашою програмою, то її виконання буде припинено.

Така поведінка оператора new у разі неможливості задовольнити запит на виділення пам'яті визначається стандартом мови C++. На таку реалізацію налаштовані також всі сучасні компілятори, в т.ч. останні версії Visual C++ і C++ Builder. Однак йдеться про те, що деякі старіші компілятори обробляють new -настанови дещо по-іншому. Відразу після винайдення мови C++ оператор new під час невдалого виконання повертав нульовий покажчик. Пізніше його реалізація була змінена так, щоб у разі невдачі генерувався виняток, як це було описано вище. Оскільки у цьому навчальному посібнику ми дотримуємося стандарту мови C++, то в усіх представлених тут прикладах передбачається саме генерування винятку. Якщо ж Ви використаєте старіший компілятор, то зверніться до документації, що додається до нього, і уточніть, як реалізований оператор new (у разі потреби внесіть в приклади відповідні зміни).

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

Розглянемо приклад програми, яка ілюструє використання операторів new і delete.

Код програми 9.15. Демонстрація динамічного розподілу пам'яті

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main ()

{

int *p;

 

p = new int; // Виділяємо пам'ять для int -значення.

*p = 20; // Поміщаємо в цю область пам'яті значення 20.

cout << *p; // Переконуємося (шляхом виведення на екран) в

// роботоздатності цього коду програми.

delete p; // Звільняємо пам'ять.

 

getch (); return 0;

}

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

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

9.5.2. Ініціалізація динамічно виділеної пам'яті

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

Код програми 9.16. Демонстрація ініціалізації динамічно виділеної пам'яті

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

 

int main ()

{

int *p;

p = new int (99); // Ініціалізується пам'ять числом 99.

cout << *p; // На екран виводиться число 99.

delete p;

 

getch (); return 0;

}

9.5.3. Динамічне виділення пам'яті для масивів

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

змінна-покажчик = new тип [ розмір ];

У цьому записі елемент розмір задає кількість елементів у масиві.

Щоб звільнити пам'ять, виділену для динамічно створеного масиву, використовують такий формат оператора delete:

delete [] змінна-покажчик;

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

Необхідно пам'ятати! Старіші версії С++-компілятора можуть вимагати задавання розміру масиву, що видаляється, оскільки в ранніх версіях мови C++ для звільнення пам'яті, займаної масивом, що видаляється, необхідно було застосовувати такий формат оператора delete:

delete [ розмір ] змінна-покажчик;

У цьому записі елемент розмір задає кількість елементів у масиві. Стандарт мови програмування C++ більше не вимагає вказувати розмір при його видаленні.

У процесі виконання наведеної нижче програми виділяється пам'ять для 10-елементного масиву типу double, який потім заповнюється значеннями від 100 до 109, після чого вміст цього масиву відображається на екрані.

Код програми 9.17. Демонстрація виділення пам'яті для масивів

#include <vcl>

#include <iostream> // Для потокового введення-виведення

#include <conio> // Для консольного режиму роботи

using namespace std; // Використання стандартного простору імен

 

int main ()

{

double *p;

int i;

 

p = new double [10]; // Виділення пам'яті для 10-елем. масиву.

 

// Заповнюємо масив значеннями від 100 до 109.

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

 

// Відображаємо вміст масиву.

cout << "Ініціалізований масив <p>\n";

for (i=0; i<10; i++) cout << p[i] << " ";

cout << "\n";

 

delete [] p; // Видалення всього масиву.

 

cout << "Видалений масив <p>\n";

for (i=0; i<10; i++) cout << p[i] << " ";

cout << "\n";

 

getch (); return 0;

}

Необхідно пам'ятати! При динамічному виділенні пам'яті для масиву його не можна одночасно ініціалізувати.

9.5.4. Функції виділення та звільнення пам'яті у мові програмування С

Мова програмування C не містить операторів new або delete. Замість них у мові С використовують бібліотечні функції, призначені для виділення і звільнення пам'яті. З метою сумісності, мова C++, як і раніше, підтримує С-систему динамічного розподілу пам'яті і не дарма: у С++-програмах все ще використовують С-орієнтовані засоби динамічного розподілу пам'яті. Тому їм варто приділити певну увагу.

Ядро С-системи динамічного розподілу пам'яті становлять функції malloc () і free (). Функція malloc () призначена для виділення пам'яті, а функція free () – для її звільнення. Іншими словами, кожного разу, коли за допомогою функції malloc () робиться запит, частина вільної пам'яті виділяється відповідно до цього запиту. Під час кожного виклику функції free () відповідна область пам'яті повертається системі. Будь-яка програма, яка використовує ці функції, повинна містити заголовок < cstdlib >.

Функція malloc () має такий прототип:

void * malloc (size _t num_bytes);

У цьому записі num_bytes означає кількість байтів запрошуваної пам'яті. Тип size _t є різновид цілочисельного типу без знаку. Функція malloc () повертає покажчик типу void, який відіграє роль узагальненого покажчика. Щоб з цього узагальненого покажчика отримати покажчик на потрібний Вам тип, необхідно використовувати операцію приведення типів. Внаслідок успішного виклику функція malloc () поверне покажчик на перший байт області пам'яті, виділеної з "купи". Якщо для задоволення запиту вільної пам'яті в системі недостатньо, функція malloc () повертає нульовий покажчик.

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

void free (void * ptr);

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

Використання функцій malloc () і free () продемонстровано у наведеному нижче коді програми.

Код програми 9.18. Демонстрація механізму використання функцій malloc() і free()

#include <iostream> // Для потокового введення-виведення

#include <cstdlib> // Для використання бібліотечних функцій

using namespace std; // Використання стандартного простору імен

 

int main ()

{

int *izm;

double *jzm;

 

izm = (int *) malloc (sizeof (int)};

if (!izm) {

cout << "Виділити пам'ять не вдалося.\n";

return 1;

}

 

jzm = (double *) malloc (sizeof (double));

if (!jzm) {

cout << "Виділити пам'ять не вдалося.\n";

return 1;

}

*izm = 10;

*jzm = 100.123;

cout << *izm << " " << *jzm;

 

// Звільнення пам'яті.

free (izm);

free (jzm);

 

getch (); return 0;

}

Хоча функції malloc () і free () повністю придатні для динамічного розподілу пам'яті, є ряд причин, згідно з якими у мові програмування C++ визначено власні засоби динамічного розподілу пам'яті:

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

по-друге, С++-оператор new автоматично повертає коректний тип покажчика, що звільняє програміста від потреби використовувати операцію приведення типів;

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

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

І останнє. Через можливість несумісності не варто змішувати функції malloc () і free () з операторами new і delete в одній програмі.

9.6. Зведена таблиця пріоритетів виконання С++-операторів

У табл|. 9.3 показано пріоритет виконання всіх С++-операторів (від вищого до найнижчого). Більшість операторів асоційована зліва направо. Але унарні оператори, оператори присвоєння і оператор "?" асоційовані справа наліво. Звернемо Вашу увагу на те, що ця таблиця містить декілька операторів, які ми поки не використовували в наших прикладах, оскільки вони належать до об'єктно-орієнтованого програмування (і описані нижче).

Табл. 9.3. Пріоритет С++--операторів

Найвищий () [] ->::.
  ! ~ ++ -- - * & sizeof new delete typeid оператори приведення типу
  .* ->*
  * / %
  +
  << >>
  < <= > >=
  ==!=
  &
  ^
  ï
  &&
  ïï
  ?:
  = += -= *= /= %= >>= <<= &= ^= |=
Нижчий ,


Розділ 10. Поняття про структури
і об'єднання даних

У мові програмування C++ визначено декілька складених типів даних, тобто типів, які складаються з двох або більше елементів. Такі складені типи даних як масив і перерахунок розглядалися раніше відповідно у розд. 6.4 і 9.4. З двома іншими – структурами і об'єднаннями – ми познайомимося у цьому розділі, а знайомство з ще одним складеним типом – класом – ми відкладемо до розд. 12. І, хоча структуровані та об'єднані типи даних призначено для задоволення різних програмних потреб, обидва вони є зручними засобами керування групами взаємопов'язаних між собою змінних. При цьому важливо розуміти, що створення структури (або об'єднання) означає створення нового визначеного програмістом типу даних. Сама можливість створення власних типів даних є ознакою потужності мови C++.




Поделиться с друзьями:


Дата добавления: 2014-11-29; Просмотров: 457; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.115 сек.