Урок №58. Неявна конвертація типів даних

  Юрій  | 

  Оновл. 4 Вер 2021  | 

 339

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

Конвертація типів

Різні типи даних можуть представляти одне значення по-різному, наприклад, значення 4 типу int і значення 4.0 типу float зберігаються як абсолютно різні двійкові шаблони.

І як ви думаєте, що станеться, якщо зробити наступне:

Тут компілятор не зможе просто скопіювати біти зі значення 4 типу int і перемістити їх в змінну f типу float. Замість цього йому потрібно буде перетворити ціле число 4 в значення типу з плаваючою крапкою, яке потім можна буде присвоїти змінній f.

Процес конвертації значення з одного типу даних в інший називається конвертацією типу. Конвертація типу може виконуватися в наступних випадках:

Випадок №1: Присвоювання або ініціалізація змінної значенням іншого типу даних:

Випадок №2: Передача значення в функцію, де тип параметру — інший:

Випадок №3: Повернення з функції, де тип значення, що повертається, — інший:

Випадок №4: Використання арифметичного оператору з операндами різних типів:

У всіх цих випадках (і в багатьох інших) мова C++ буде використовувати конвертацію типів.

Є 2 основних способи конвертації типів:

   Неявна конвертація типів, коли компілятор автоматично конвертує один фундаментальний тип даних в інший.

   Явна конвертація типів, коли розробник використовує один з операторів конвертації для виконання конвертації об’єкта одного типу даних в інший.

Неявна конвертація типів

Неявна конвертація типу (або “автоматичне приведення типу”) виконується всякий раз, коли потрібен один фундаментальний тип даних, але надається інший, і користувач не вказує компілятору, як виконати конвертацію (не використовує явну конвертацію типів через оператори конвертації).

Є 2 основних способи неявної конвертації типів даних:

   числове розширення;

   числова конверсія.

Числове розширення

Коли значення з одного типу даних конвертується в інший тип даних, який є більшим (за розміром і по діапазону значень), то це називається числовим розширенням. Наприклад, тип int може бути розширений в тип long, а тип float може бути розширений в тип double:

В мові C++ є 2 типи розширень:

   Інтегральне розширення (або “цілочисельне розширення”). Включає в себе конвертацію цілочисельних типів, менших, ніж int (bool, char, unsigned char, signed char, unsigned short, signed short) в int (якщо це можливо) або в unsigned int.

   Розширення типу з плаваючою крапкою. Конвертація з типу float в тип double.

Інтегральне розширення і розширення типу з плаваючою крапкою використовуються для перетворення “менших за розміром” типів даних в типи int/unsigned int чи double (вони найбільш ефективні для виконання різних операцій).

Важливо: Числові розширення завжди безпечні і не приводять до втрати даних.

Числові конверсії

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

На відміну від розширень, які завжди є безпечними, конверсії можуть (але не завжди) призвести до втрати даних. Тому в будь-якій програмі, де виконується неявна числова конверсія, компілятор видаватиме попередження.

Є багато правил щодо виконання числової конверсії, але ми розглянемо тільки основні.

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

Тут ми присвоїли величезне цілочисельне значення типу int змінній типу char (діапазон якого складає від -128 до 127). Це призведе до переповнення і наступного результату:

48

Однак, якщо число підходить по діапазону, то конвертація пройде успішно. Наприклад:

Тут ми отримаємо очікуваний результат:

3
0.1234

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

В цьому випадку ми спостерігаємо втрату в точності, тому що точність типу float менша, ніж типу double:

0.123456791

Конвертація з типу int в тип float успішна до тих пір, поки значення підходять по діапазону, наприклад:

Результат:

10

Аналогічно, конвертація з типу float в тип int успішна до тих пір, поки значення підходять по діапазону. Але слід пам’ятати, що будь-який дріб відкидається, наприклад:

Дрібна частина значення (.6) ігнорується, тому результат:

4

Обробка арифметичних виразів

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

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

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

Пріоритет типів операндів:

   long double (найвищий);

   double;

   float;

   unsigned long long;

   long long;

   unsigned long;

   long;

   unsigned int;

   int (найнижчий).

Ми можемо використовувати оператор typeid (який знаходиться в заголовку typeinfo), щоб дізнатися вирішальний тип в виразі.

В наступному прикладі у нас є 2 змінні типу short:

Оскільки значеннями змінних типу short є цілі числа і тип short менше (за розміром/по діапазону) типу int, то він піддається інтегральному розширенню в тип int. Результатом додавання двох int-ів буде тип int:

int 9

Розглянемо інший випадок:

Тут short піддається інтегральному розширенню в int. Однак int і double як і раніше не збігаються. Оскільки double знаходиться вище в ієрархії типів, то ціле число 2 перетворюється в 2.0 (тип double), і два double дорівнюють double:

double 5

З цією ієрархією іноді можуть виникати цікаві ситуації, наприклад:

Очікується, що результатом виразу 5u − 10 буде -5, оскільки 5 − 10 = -5, але результат:

4294967291

Тут значення signed int (10) піддається розширенню в unsigned int (який має більш високий пріоритет), і вираз обчислюється як unsigned int. А оскільки unsigned — це тільки додатні числа, то відбувається переповнення, і ми маємо, що маємо.

Це одна з тих вагомих причин, чому слід уникати використання типу unsigned int взагалі.

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (7 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *