Приведення типів

Приведення (перетворення) типів (англ. type conversion, typecasting, coercion) — в програмуванні це зміна типу сутності одного типу даних в інший, що може відбуватися різними способами, явно чи неявно.

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

Кожна мова програмування має свої правила приведення типів даних. Загалом, перетворювати можна і об'єктні типи і основні типи даних. В більшості мов, слово coercion використовується позначення неявних перетворень типів, або під час компіляції або під час виконання програми. Типовим прикладом може бути вираз де в одному виразі присутні цілі числа і дробові числа з рухомою комою (такому як 5 + 0,1), в якому цілі числа як правило перетворюються в дробові. Явне перетворення типів може виконуватись або за допомогою вбудованих процедур (або спеціальних синтаксичних конструкцій) мови програмування або за допомогою окремо визначених процедур перетворення, таких як перевантажений конструктор об'єкту.

В більшості мов сімейства ALGOL з можливістю визначати вкладені функції, таких як Ada, Паскаль, Object Pascal і Modula 2, conversion (перетворення) і casting (приведення) це два окремі поняття. В цих мовах, перетворення відноситься до явного або неявного перенесення значення з одного типу даних в інший, наприклад 16-розрядне ціле число в 32-розрядне. Вимоги щодо зберігання змінної в новому форматі можуть змінитися в результаті перетворення. Може відбуватися втрата точності або відсікання молодших розрядів. Поняття приведення, в свою чергу, відноситься до явного змінення інтерпретації способу збереження в бітах при переведенні значення з одного типу в інший. Наприклад 32 послідовних біта можуть представлятися у вигляді масиву із 32 елементів типу boolean, а рядок в 4 байти може представлятися як 32 бітне ціле або число з рухомою комою одинарної точності. Оскільки вимоги до зберігання значень не змінюються, таке перетворення все одно вимагає знань про низькорівневу організацію даних в конкретному форматі, порядок байтів, вирівнювання для того, щоб розуміти що відбувається.

В родині мов програмування C і ALGOL 68, поняття cast приведення зазвичай має зміст явного перетворення типів, незалежно від того, чи змінює це спосіб збереження біт або є реальним перетворенням.

Перетворення типів в мові C

Неявне перетворення типу

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

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

double d;
long l;
int i;

if (d > i) d = i;
if (i > l) l = i;
if (d == l) d *= 2;

Хоча змінні d, l і i належать до різних типів даних, вони будуть автоматично перетворюватись в один тип даних кожен раз коли буде виконуватись операція порівняння або присвоювання. Таке неявне приведення типів може привести до небажаних наслідків. При перетворенні чисел з рухомою комою в цілі може відбуватися втрата точності, оскільки дробова частина числа буде відкинута (з округленням в бік нуля). І навпаки, при перетворенні цілого значення до числа з рухомою комою також може відбуватися втрата точності, оскільки тип, що представляє дрібне число не може вмістити і представити точно дане ціле число (наприклад, тип float в стандарті IEEE 754 може мати одинарну точність, яка не може коректно представити ціле число 16777217, в той час як 32-ух бітний тип integer може). Це може привести до незрозумілої поведінки, як показано в наступному коді:

#include <stdio.h>

int main(void)
{
    int i_value   = 16777217;
    float f_value = 16777216.0;
    printf("Ціле (integer): %d\n", i_value);
    printf("З рухомою комою (float):   %f\n", f_value);
    printf("Їх рівність: %d\n", i_value == f_value);
}

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

    Ціле (integer): 16777217
    З рухомою комою (float): 16777216.000000
    Їх рівність: 1

Зверніть увагу, що на екрані буде виведено 1 в останньому рядку, це означає, що числа вважаються рівними при порівнянні. Ця дивна поведінка викликана неявним перетворенням i_value у тип float при порівнянні числа з f_value. Перетворення приводить до втрати точності, що робить значення рівними перед операцією порівняння.

Важливі примітки:

  1. Приведення float до int призводить до усічення, тобто відкидання дробової частини;
  2. Приведення double до float призводить до округлення числа;
  3. Приведення long до int призводить до втрати надлишкових верхніх розрядів.

Розширення типів

Окремим випадком неявного приведення типів є розширення типу, при якому компілятор автоматично розширяє двійкове представлення об'єктів цілого типу або типу з рухомою комою. Розширення зазвичай відбувається із типами, які менші ніж вбудований тип АЛП цільової платформи, на якій виконується програма. Це відбувається перед арифметичними і логічними операціями для того, щоб ці операції стали можливими, або більш ефективними, у випадку коли АЛП, може працювати більше ніж з одним типом даних. C і C++ виконують таке розширення для об'єктів логічного, символьного, широкого символьного, перечислення, і короткого цілого типів, які перетворюються в int, і для об'єктів типу float, які розширюються до типу double. На відміну від інших від інших перетворень типів, розширення ніколи не призводить до втрати точності або зміни значення, збереженого в змінній.

Явне перетворення типу

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

double da = 3.3;
double db = 3.3;
double dc = 3.4;
int result = (int)da + (int)db + (int)dc; //result == 9
//якби тут відбулося неявне перетворення типів (як при написанні виразу "result = da + db + dc"), результат би дорівнював 10

Існує кілька видів явного перетворення.

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

В об’єктно-орієнтованому програмуванні, об'єкти також можна приводити з пониженням типів: посилання на базовий клас може вказувати на його похідні (дочірні) класи.

Використання перевантаженого конструктора об'єкта

class Myclass {
public:
    double myD;
    Myclass(double d) : myD(d) {};
};

int main(int argc, char *argv[])
{
    Myclass obj = 5.2; // Це перетворення типів
    return 0;
}

Джерела

Посилання