Каламбур типізації (англ. type punning) — термін в інформатиці для позначення різних технік порушення або «обману» системи типів деякої мови програмування, які мають ефект, який було б складно або неможливо забезпечити в рамках формальної мови.
Мови C і надають явні можливості каламбуру типізації за допомогою таких конструкцій, як зведення типів, union
, а також reinterpret_cast
для , хоча стандарти цих мов деякі випадки таких каламбурів трактують як невизначену поведінку.
У мові Pascal записи з варіантами дозволяють інтерпретувати конкретний тип даних більш, ніж в один спосіб, або навіть у спосіб, не передбачений мовою.
Каламбур типізації є прямим порушенням типобезпеки. Традиційно можливість побудувати каламбур типізації пов'язують зі слабкою типізацією, але й деякі сильно типізовані мови або їх реалізації надають такі можливості (як правило, використовуючи у пов'язаних з ними ідентифікаторах слова unsafe
або unchecked
). Прихильники типобезпеки стверджують, що «необхідність» каламбурів типізації є міфом.
Приклади
Рядки та числа в JavaScript
JS дозволяє неявне зведення типів між рядками та числами, що може призводити до нелогічних результатів, наприклад:
console.log(2 + 2) // 4 console.log("2" + "2") // "22" console.log(2 + 2 - 2) // 2 console.log("2" + "2" - "2") // "20"
Оператор +
для чисел працює як додавання, а для рядків як конкатенація, проте оператор -
працює тільки як віднімання для чисел, тому в останньому виразі ми отримуємо "22" - "2"
, що призводить до значення 20
.
Порівняння в JavaScript
Порівняння між значеннями різних типів JS не транзитивне:
0 == "0" 0 == [] "0" != []
Сокети в C
Класичний приклад каламбуру типізації можна побачити в інтерфейсі сокетів Берклі. Функція, яка пов'язує відкритий неініціалізований сокет з IP-адресою, має таку сигнатуру:
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
Функцію bind
зазвичай викликають так:
struct sockaddr_in sa = {0}; int sockfd = ...; sa.sin_family = AF_INET; sa.sin_port = htons(port); bind(sockfd, (struct sockaddr *)&sa, sizeof sa);
Бібліотека сокетів Берклі у своїй основі спирається на той факт, що в мові C вказівник на struct sockaddr_in
може безперешкодно перетворюватися на вказівник на struct sockaddr
, а також що обидва типи частково збігаються щодо організації (подання в пам'яті). Отже, вказівник на поле my_addr->sin_family
(де my_addr
має тип struct sockaddr*
) насправді вказуватиме на поле sa.sin_family
(де sa
має тип struct sockaddr_in
). Іншими словами, бібліотека використовує каламбур типізації для реалізації примітивної форми наслідування.
У програмуванні часто зустрічається використання "прошарків", що дозволяють ефективно зберігати різні типи даних у єдиному блоці пам'яті. Найчастіше такий трюк використовують для взаємно виключних даних із метою оптимізації.
Числа з рухомою комою
Припустимо, потрібно перевірити, що число з рухомою комою є від'ємним. Можна було б написати:
bool is_negative(float x) { return x < 0.0; }
Однак, порівняння чисел із рухомою комою є ресурсомісткими, оскільки діє в особливий спосіб для NaN. Узявши до уваги, що тип float
подано згідно стандарту , а тип int
має розмір 32 біти і за знак у ньому відповідає той самий біт, що й у float
, можна для отримання знакового біту числа з рухомою комою застосувати каламбур типізації, використавши тільки цілочисельне порівняння:
bool is_negative(float x) { return *((int*)&x) < 0; }
Така форма каламбуру типізації є найнебезпечнішою. Попередній приклад спирався лише на гарантії, надані мовою C щодо подання та перетворюваності вказівників; однак, цей приклад спирається на припущення щодо конкретного апаратного забезпечення. У деяких випадках, наприклад, під час розробки програм реального часу, яких компілятор не здатний оптимізувати самостійно, такі небезпечні програмні рішення виявляються необхідними. У таких випадках забезпечити підтримуваність коду допомагають коментарі та перевірки часу компіляції.
Реальний приклад можна знайти в коді Quake III — див. Швидкий обернений квадратний корінь.
На додаток до припущень про бітове подання чисел з рухомою комою наведений вище приклад каламбуру типізації також порушує встановлені мовою C правила доступу до об'єктів: x
оголошено як float
, але його значення зчитується у виразі, що має тип signed int
. На багатьох поширених платформах такий каламбур типізації вказівників може призвести до проблем, якщо вказівники по-різному вирівняно в пам'яті. Більш того, вказівники різного розміру можуть здійснювати спільний доступ до певних дільнок пам'яті, спричиняючи помилки, яких не може виявити компілятор.
Використання union
Проблему суміщення назв можна вирішити за допомогою union
(хоча приклад нижче ґрунтується на припущенні, що число з рухомою комою подано за стандартом ):
bool is_negative(float x) { union { unsigned int ui; float d; } my_union = { .d = x }; return (my_union.ui & 0x80000000) != 0; }
Це код на C99 з використанням позначених ініціалізаторів (англ. Designated initialisers). При створенні об'єднання ініціалізується його дійсне поле, а потім відбувається читання значення цілого поля (фізично розміщеного в пам'яті на тій самій адресі), згідно з пунктом s6.5 стандарту. Деякі компілятори підтримують такі конструкції як розширення мови, наприклад, GCC.
Як ще один приклад каламбуру типізації див. [en].
Паскаль
Варіантний запис дозволяє розглядати тип даних по-різному, залежно від зазначеного варіанту. У цьому прикладі передбачається, що integer
має розмір 16 біт, longint
і real
— 32 біти, а character
— 8 біт:
type variant_record = record case rec_type : longint of 1: ( I : array [1..2] of integer ); 2: ( L : longint ); 3: ( R : real ); 4: ( C : array [1..4] of character ); end; Var V: Variant_record; K: Integer; LA: Longint; RA: Real; Ch: character; ... V.I := 1; Ch := V.C[1]; (* Отримуємо перший байт поля V.I *) V.R := 8.3; LA := V.L; (* Зберігаємо дійсне число в цілочисельну комірку *)
У Паскалі копіювання дійсного на ціле перетворює його в округлене значення. Цей метод перетворює двійкове значення числа з рухомою комою на щось, що має довжину довгого цілого (32 біти), що не тотожне і навіть може бути несумісним із довгими цілими на деяких платформах. Подібні приклади можуть використовуватися для дивних перетворень, однак у деяких випадках такі конструкції можуть мати сенс, наприклад, для обчислення розташування певних фрагментів даних. У цьому прикладі передбачається, що вказівник і довге ціле мають розмір 32 біти:
Type PA = ^Arec; Arec = record case rt : longint of 1: (P: PA); 2: (L: Longint); end; Var PP: PA; K: Longint; ... New(PP); PP^.P := PP; Writeln('Змінна PP міститься в пам''яті за адресою ', hex(PP^.L));
Стандартна процедура New
в Паскалі призначена для динамічного виділення пам'яті для вказівника, а під hex
мається на увазі певна процедура, що друкує шістнадцяткове подання цілого числа. Це дозволяє вивести на екран адресу вказівника, що зазвичай заборонено (вказівники в Паскалі можна лише присвоювати, але не читати чи виводити). Присвоєння значення цілому варіанту вказівника дозволяє читати та змінювати будь-яку ділянку системної пам'яті:
PP^.L := 0; PP := PP^.P; (* PP вказує на адресу 0 *) K := PP^.L; (* K містить значення слова за адресою 0 *) Writeln(' Слово за адресою 0 цієї машини містить ', K);
Ця програма може працювати коректно або впасти, якщо адресу 0 захищено від читання, залежно від операційної системи.
Див. також
Примітки
- Lawrence C. Paulson. ML for the Working Programmer. — 2nd. — Cambridge, Great Britain : Cambridge University Press, 1996. — С. 2. — (тверда обкладинка), 0-521-56543-X (м'яка обкладинка).
- struct sockaddr_in, struct in_addr. www.gta.ufrj.br. оригіналу за 24 січня 2016. Процитовано 17 січня 2016.
- ISO/IEC 9899:1999 s6.5/7
- GCC: Non-Bugs. оригіналу за 22 листопада 2014. Процитовано 21 листопада 2014.
Посилання
- Теорія
- Benjamin C. Pierce. Types and Programming Languages. — MIT Press, 2002. — .
- Розділ посібника з компілятора GCC щодо опції -fstrict-aliasing, що запобігає деяким каламбурам типізації
- Defect Report 257, що випадково визначає «каламбур типізації» за допомогою
union
і обговорює поведінку наведеного вище коду, що залежить від реалізації . - Defect Report 283 про використання типу
union
для каламбурів типізації
- Типобезпечні мови
- Unchecked Conversion (мова Ada)
- The Unsafe structure (мова Standard ML)
- Haskell unsafeCoerce (мова Haskell)
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Kalambur tipizaciyi angl type punning termin v informatici dlya poznachennya riznih tehnik porushennya abo obmanu sistemi tipiv deyakoyi movi programuvannya yaki mayut efekt yakij bulo b skladno abo nemozhlivo zabezpechiti v ramkah formalnoyi movi Movi C i C nadayut yavni mozhlivosti kalamburu tipizaciyi za dopomogoyu takih konstrukcij yak zvedennya tipiv a href wiki D0 9E D0 B1 27 D1 94 D0 B4 D0 BD D0 B0 D0 BD D0 BD D1 8F D1 81 D1 82 D1 80 D1 83 D0 BA D1 82 D1 83 D1 80 D0 B0 D0 B4 D0 B0 D0 BD D0 B8 D1 85 title Ob yednannya struktura danih union a a takozh reinterpret cast dlya C hocha standarti cih mov deyaki vipadki takih kalamburiv traktuyut yak neviznachenu povedinku U movi Pascal zapisi z variantami dozvolyayut interpretuvati konkretnij tip danih bilsh nizh v odin sposib abo navit u sposib ne peredbachenij movoyu Kalambur tipizaciyi ye pryamim porushennyam tipobezpeki Tradicijno mozhlivist pobuduvati kalambur tipizaciyi pov yazuyut zi slabkoyu tipizaciyeyu ale j deyaki silno tipizovani movi abo yih realizaciyi nadayut taki mozhlivosti yak pravilo vikoristovuyuchi u pov yazanih z nimi identifikatorah slova unsafe abo unchecked Prihilniki tipobezpeki stverdzhuyut sho neobhidnist kalamburiv tipizaciyi ye mifom PrikladiRyadki ta chisla v JavaScript JS dozvolyaye neyavne zvedennya tipiv mizh ryadkami ta chislami sho mozhe prizvoditi do nelogichnih rezultativ napriklad console log 2 2 4 console log 2 2 22 console log 2 2 2 2 console log 2 2 2 20 Operator dlya chisel pracyuye yak dodavannya a dlya ryadkiv yak konkatenaciya prote operator pracyuye tilki yak vidnimannya dlya chisel tomu v ostannomu virazi mi otrimuyemo 22 2 sho prizvodit do znachennya 20 Porivnyannya v JavaScript Porivnyannya mizh znachennyami riznih tipiv JS ne tranzitivne 0 0 0 0 Soketi v C Klasichnij priklad kalamburu tipizaciyi mozhna pobachiti v interfejsi soketiv Berkli Funkciya yaka pov yazuye vidkritij neinicializovanij soket z IP adresoyu maye taku signaturu int bind int sockfd struct sockaddr my addr socklen t addrlen Funkciyu bind zazvichaj viklikayut tak struct sockaddr in sa 0 int sockfd sa sin family AF INET sa sin port htons port bind sockfd struct sockaddr amp sa sizeof sa Biblioteka soketiv Berkli u svoyij osnovi spirayetsya na toj fakt sho v movi C vkazivnik na struct sockaddr in mozhe bezpereshkodno peretvoryuvatisya na vkazivnik na struct sockaddr a takozh sho obidva tipi chastkovo zbigayutsya shodo organizaciyi podannya v pam yati Otzhe vkazivnik na pole my addr gt sin family de my addr maye tip struct sockaddr naspravdi vkazuvatime na pole sa sin family de sa maye tip struct sockaddr in Inshimi slovami biblioteka vikoristovuye kalambur tipizaciyi dlya realizaciyi primitivnoyi formi nasliduvannya U programuvanni chasto zustrichayetsya vikoristannya prosharkiv sho dozvolyayut efektivno zberigati rizni tipi danih u yedinomu bloci pam yati Najchastishe takij tryuk vikoristovuyut dlya vzayemno viklyuchnih danih iz metoyu optimizaciyi Chisla z ruhomoyu komoyu Pripustimo potribno pereviriti sho chislo z ruhomoyu komoyu ye vid yemnim Mozhna bulo b napisati bool is negative float x return x lt 0 0 Odnak porivnyannya chisel iz ruhomoyu komoyu ye resursomistkimi oskilki diye v osoblivij sposib dlya NaN Uzyavshi do uvagi sho tip float podano zgidno standartu a tip int maye rozmir 32 biti i za znak u nomu vidpovidaye toj samij bit sho j u float mozhna dlya otrimannya znakovogo bitu chisla z ruhomoyu komoyu zastosuvati kalambur tipizaciyi vikoristavshi tilki cilochiselne porivnyannya bool is negative float x return int amp x lt 0 Taka forma kalamburu tipizaciyi ye najnebezpechnishoyu Poperednij priklad spiravsya lishe na garantiyi nadani movoyu C shodo podannya ta peretvoryuvanosti vkazivnikiv odnak cej priklad spirayetsya na pripushennya shodo konkretnogo aparatnogo zabezpechennya U deyakih vipadkah napriklad pid chas rozrobki program realnogo chasu yakih kompilyator ne zdatnij optimizuvati samostijno taki nebezpechni programni rishennya viyavlyayutsya neobhidnimi U takih vipadkah zabezpechiti pidtrimuvanist kodu dopomagayut komentari ta perevirki chasu kompilyaciyi Realnij priklad mozhna znajti v kodi Quake III div Shvidkij obernenij kvadratnij korin Na dodatok do pripushen pro bitove podannya chisel z ruhomoyu komoyu navedenij vishe priklad kalamburu tipizaciyi takozh porushuye vstanovleni movoyu C pravila dostupu do ob yektiv x ogolosheno yak float ale jogo znachennya zchituyetsya u virazi sho maye tip signed int Na bagatoh poshirenih platformah takij kalambur tipizaciyi vkazivnikiv mozhe prizvesti do problem yaksho vkazivniki po riznomu virivnyano v pam yati Bilsh togo vkazivniki riznogo rozmiru mozhut zdijsnyuvati spilnij dostup do pevnih dilnok pam yati sprichinyayuchi pomilki yakih ne mozhe viyaviti kompilyator Vikoristannya union Problemu sumishennya nazv mozhna virishiti za dopomogoyu union hocha priklad nizhche gruntuyetsya na pripushenni sho chislo z ruhomoyu komoyu podano za standartom bool is negative float x union unsigned int ui float d my union d x return my union ui amp 0x80000000 0 Ce kod na C99 z vikoristannyam poznachenih inicializatoriv angl Designated initialisers Pri stvorenni ob yednannya inicializuyetsya jogo dijsne pole a potim vidbuvayetsya chitannya znachennya cilogo polya fizichno rozmishenogo v pam yati na tij samij adresi zgidno z punktom s6 5 standartu Deyaki kompilyatori pidtrimuyut taki konstrukciyi yak rozshirennya movi napriklad GCC Yak she odin priklad kalamburu tipizaciyi div en Paskal Variantnij zapis dozvolyaye rozglyadati tip danih po riznomu zalezhno vid zaznachenogo variantu U comu prikladi peredbachayetsya sho integer maye rozmir 16 bit longint i real 32 biti a character 8 bit type variant record record case rec type longint of 1 I array 1 2 of integer 2 L longint 3 R real 4 C array 1 4 of character end Var V Variant record K Integer LA Longint RA Real Ch character V I 1 Ch V C 1 Otrimuyemo pershij bajt polya V I V R 8 3 LA V L Zberigayemo dijsne chislo v cilochiselnu komirku U Paskali kopiyuvannya dijsnogo na cile peretvoryuye jogo v okruglene znachennya Cej metod peretvoryuye dvijkove znachennya chisla z ruhomoyu komoyu na shos sho maye dovzhinu dovgogo cilogo 32 biti sho ne totozhne i navit mozhe buti nesumisnim iz dovgimi cilimi na deyakih platformah Podibni prikladi mozhut vikoristovuvatisya dlya divnih peretvoren odnak u deyakih vipadkah taki konstrukciyi mozhut mati sens napriklad dlya obchislennya roztashuvannya pevnih fragmentiv danih U comu prikladi peredbachayetsya sho vkazivnik i dovge cile mayut rozmir 32 biti Type PA Arec Arec record case rt longint of 1 P PA 2 L Longint end Var PP PA K Longint New PP PP P PP Writeln Zminna PP mistitsya v pam yati za adresoyu hex PP L Standartna procedura New v Paskali priznachena dlya dinamichnogo vidilennya pam yati dlya vkazivnika a pid hex mayetsya na uvazi pevna procedura sho drukuye shistnadcyatkove podannya cilogo chisla Ce dozvolyaye vivesti na ekran adresu vkazivnika sho zazvichaj zaboroneno vkazivniki v Paskali mozhna lishe prisvoyuvati ale ne chitati chi vivoditi Prisvoyennya znachennya cilomu variantu vkazivnika dozvolyaye chitati ta zminyuvati bud yaku dilyanku sistemnoyi pam yati PP L 0 PP PP P PP vkazuye na adresu 0 K PP L K mistit znachennya slova za adresoyu 0 Writeln Slovo za adresoyu 0 ciyeyi mashini mistit K Cya programa mozhe pracyuvati korektno abo vpasti yaksho adresu 0 zahisheno vid chitannya zalezhno vid operacijnoyi sistemi Div takozhOb yednannya struktura danih Zvedennya tipiv Silna i slabka tipizaciya TipobezpechnistPrimitkiLawrence C Paulson ML for the Working Programmer 2nd Cambridge Great Britain Cambridge University Press 1996 S 2 ISBN 0 521 57050 6 tverda obkladinka 0 521 56543 X m yaka obkladinka struct sockaddr in struct in addr www gta ufrj br originalu za 24 sichnya 2016 Procitovano 17 sichnya 2016 ISO IEC 9899 1999 s6 5 7 GCC Non Bugs originalu za 22 listopada 2014 Procitovano 21 listopada 2014 PosilannyaTeoriya Benjamin C Pierce Types and Programming Languages MIT Press 2002 ISBN 978 0 262 16209 8 Mova C zviti pro defekti standartu C99 Rozdil posibnika z kompilyatora GCC shodo opciyi fstrict aliasing sho zapobigaye deyakim kalamburam tipizaciyi Defect Report 257 sho vipadkovo viznachaye kalambur tipizaciyi za dopomogoyu union i obgovoryuye povedinku navedenogo vishe kodu sho zalezhit vid realizaciyi Defect Report 283 pro vikoristannya tipu union dlya kalamburiv tipizaciyi Tipobezpechni movi Unchecked Conversion mova Ada The Unsafe structure mova Standard ML Haskell unsafeCoerce mova Haskell