Цікавою характеристикою C порівняно з іншими мовами є те, що багато типів даних базуються на розмірі слова цільової архітектури, а не в конкретних значеннях. Хоча це дозволяє використовувати мову для запису коду на машинах, які можуть мати труднощі з певними типами, це дуже важко розробити код, який буде працювати послідовно в різних архітектурах. Розглянемо код:
uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;
У архітектурі, де int
16 біт (все ще стосується багатьох малих мікроконтролерів), цей код призначив би значення 1, використовуючи чітко визначену поведінку. На машинах із int
64 бітами він присвоїв би значення 4294836225, знову ж таки використовуючи чітко визначену поведінку. На машинах із int
32 бітами, ймовірно, присвоїть значення -131071 (я не знаю, чи було б це визначено впровадженням або не визначеною поведінкою). Незважаючи на те, що код не використовує нічого, окрім того, що номінально повинні бути типи "фіксованого розміру", стандарт вимагає, щоб два різних види компілятора, які використовуються сьогодні, дали два різні результати, і багато популярних сьогодні компіляторів дадуть третину.
Цей конкретний приклад дещо надуманий, оскільки я б не очікував, що в реальному коді присвоюється добутковість двох 16-бітних значень безпосередньо 64-бітовому значенню, але він був обраний як короткий приклад для показу трьох цілих способів рекламні акції можуть взаємодіяти з типовими форматами, які не мають фіксованого розміру. Існують деякі ситуації в реальному світі, коли математика для непідписаних типів повинна виконуватися за правилами математичної цілочисельної арифметики, інші, коли це потрібно, щоб вона виконувалася за правилами модульної арифметики, а деякі там, де це дійсно немає ' t матерія. Дуже багато реального коду для таких речей, як контрольні uint32_t
сумки, спираються на арифметичне обгортання моди 2³² та на можливість виконувати довільнуuint16_t
арифметичні та отримують результати, які є, як мінімум, визначаються як точні мод 65536 (на відміну від спровокування не визначеної поведінки).
Незважаючи на те, що ця ситуація, очевидно, здасться небажаною (і стане тим більше, оскільки обробка 64-бітових норм стає нормою для багатьох цілей), комітет зі стандартів C, з чого я спостерігав, вважає за краще вводити мовні функції, які вже використовуються в деякому помітному виробництві середовища, а не вигадувати їх "з нуля". Чи є помітні розширення на мові C, які дозволяли б коду вказувати не лише те, як тип буде зберігатися, але і як він повинен вести себе у сценаріях, що передбачають можливі рекламні кампанії? Я бачу щонайменше три способи розширення компілятора може вирішити такі проблеми:
Додаючи директиву, яка б доручала компілятору примушувати певні "основні" цілі типи бути певних розмірів.
Додаючи директиву, яка б доручала компілятору оцінювати різні сценарії просування так, ніби типи машини мали певні розміри, незалежно від фактичних розмірів типів у цільовій архітектурі.
Дозволяючи засоби декларування типів із специфічними характеристиками (наприклад, заявляють, що тип повинен вести себе як мод-65536 обертаюче алгебраїчне кільце, незалежно від розміру основного слова, і не повинен неявно перетворюватися на інші типи; додаючи
wrap32
до а,int
слід отримати Результат типуwrap32
незалежно від того, чиint
більший він за 16 біт, при цьому додаванняwrap32
безпосередньо до атрибутуwrap16
має бути незаконним (оскільки жоден не може перетворити на інший)
Моє власне уподобання було б третьою альтернативою, оскільки це дозволило б навіть машинам з незвичними розмірами слів працювати з великою кількістю коду, який очікує, що змінні "загортаються" так само, як і з двома розмірами; компілятору, можливо, доведеться додати інструкції з бітового маскування, щоб змусити тип вести себе належним чином, але якщо коду потрібен тип, який завершує мод 65536, краще, щоб компілятор генерував таку маскування на машинах, які потребують цього, ніж захаращувати вихідний код або просто мати такий код, непридатний для використання на машинах, де таке маскування буде потрібно. Мені цікаво, чи існують якісь поширені розширення, які б досягли портативної поведінки за допомогою будь-якого з перерахованих вище засобів, або через якісь засоби, про які я не думав.
Щоб уточнити, що я шукаю, є кілька речей; дуже помітно:
Хоча існує багато способів, за допомогою яких можна записати код, щоб забезпечити бажану семантику (наприклад, визначення макросів для виконання математики на операндах конкретного розміру, щоб отримати результат, який явно або перетворює або не робить) або принаймні запобігає небажаному семантика (наприклад, умовно визначте тип
wrap32_t
дляuint32_t
компіляторів, деuint32_t
не отримуватиметься просування, і врахуйте, що краще для коду, який вимагаєwrap32_t
невдалої компіляції на машинах, де цей тип буде просунутий, ніж примусити його працювати і спричинити помилкову поведінку), якщо є який-небудь спосіб написання коду, який би найбільш сприятливо грав у майбутніх розширеннях мови, використовуючи це було б краще, ніж придумувати власний підхід.У мене є досить грунтовні ідеї, як можна розширити мову, щоб вирішити багато проблем із цілим розміром, дозволяючи коду отримувати однакову семантику на машинах з різними розмірами слів, але перш ніж витратити якийсь значний час на їх написання, я хотів би знати, які зусилля в цьому напрямку вже вжиті.
Я жодним чином не бажаю, щоб мене розглядали як зневагу до Комітету зі стандартів C чи роботи, яку вони виробили; Однак я очікую, що протягом декількох років стане необхідним змусити код працювати правильно на машинах, де "природний" тип просування складе 32 біта, а також на тих, де це буде 64 біт. Я думаю, що при деяких скромних розширеннях до мови (скромніших, ніж у багатьох інших змінах між C99 і C14) можна було б не тільки забезпечити чіткий спосіб ефективного використання 64-бітових архітектур, але і в угоді також полегшити взаємодію з машини "незвичайного розміру слова", стандарт яких історично схилявся назад, щоб підтримати [наприклад, дозволяючи машинам з 12-бітовим char
кодом запускати код, який очікуєuint32_t
обернути мод 2³²]. Залежно від напрямку майбутнього розширення, я б також очікував, що має бути можливість визначити макроси, які дозволять коду, написаному сьогодні, бути корисним для сьогоднішніх компіляторів, де типи цілих чисел за замовчуванням поводяться як "очікувані", але також можуть бути використані для майбутніх компіляторів, де ціле число типи за замовчуванням поводяться по-різному, але де можна надати необхідну поведінку.
int
, але він все одно прокрадається. (Знову припускаю, що моє розуміння стандарту С є правильним.)
int
воно більше, ніжuint16_t
операнди множення будуть просунуті,int
а множення буде здійснено якint
множення, і отриманеint
значення буде перетворенеint64_t
в ініціалізаціюwho_knows
.