Питання Що робить C ??! ??! оператор робити?


Я побачив лінію C, яка виглядала так:

!ErrorHasOccured() ??!??! HandleError();

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

Я ніколи не бачив цього ??!??! раніше на будь-якій мові програмування, і я не можу знайти документацію для нього де завгодно. (Google не допомагає з пошуковими термінами, як ??!??!) Що це робить і як працює зразок коду?


1605
2017-10-19 16:56


походження


Вирази свої емоції іншим способом, не використовуйте триграфів, пишіть код, який може бути зрозумілим людям - David Heffernan
@PeterOlson, як ти очікуєш? !ErrorHasOccurred() ??!???! HandleError(); скласти? Це так ??!  ???  !. Доказує точку? - Michael Kjörling
Я пропоную вам прочитати чистий код. ErrorHasOccured () слід переробити до ErrorHasNotOccured (), таким чином очищуючи знак оклику ... хто має час зрозуміти всіх цих операторів! - KadekM
Я досить предпочитаю ErrorHasOccured() && HandleError() я сам Це теж, як це робить Луа. - Hugo Zink
@ KadekM, переміщення заперечення в назву функції не робить для чистого коду, а навпаки. - marcelm


Відповіді:


??! це трифрагма що перекладається на |. Тому він говорить:

!ErrorHasOccured() || HandleError();

який через коротке замикання еквівалентний:

if (ErrorHasOccured())
    HandleError();

Гуру Тижня (стосується C ++, але актуально тут), де я його вибрав.

Можливе виникнення триграфів або як @DwB зазначає в коментарях, це, швидше за все, через EBCDIC важко (знову). Це Обговорення на форумі IBM developerworks, здається, підтримує цю теорію.

З ISO / IEC 9899: 1999, п. 5.2.1.1, виноски 12 (h / t @ Random832):

Трифрагові послідовності дозволяють вводити символи, не визначені в інваріантному наборі коду як   описаний в ISO / IEC 646, який є підмножиною семибітного коду американського ASCII-коду.


1320
2017-10-19 16:58



Тригери спочатку були потрібні у випадку, якщо у вас немає клавіатури, наприклад, '|' символ Ось це або програміст, навмисне дратує, або якась дивна функція редактора " - Martin Beckett
Так, це еквівалентно if (ErrorHasOccured()) HandleError(). На щастя, ви зазвичай зустрічаєтеся з цим ідіомом у коді perl. - user786653
Це не обов'язково EBCDIC - набір символів, що вимагає триграфів, майже точно відповідає набору символів, які не є інваріантними в ISO-646 (тобто старі 'national ascii' стандарти). - Random832
Ідеально зрозуміла альтернатива ErrorHasOccurred() && HandleError(); Тобто, якщо ви звикли працювати зі скриптами. :) - Yam Marcovic
Прочитайте його як "Either No ErrorHasOcurred" або ви повинні HandleError ", @ SparkyRobinson. - Omar Antolín-Camarena


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

Все почалося півстоліття тому, з використанням друкованих терміналів зв'язку як комп'ютерних інтерфейсів користувача. У початковій Unix і C епоху, який був ASR-33 Teletype.

Цей пристрій був повільним (10 cps), шумно і потворно, а його погляд на набір символів ASCII закінчився на 0x5f, тому він мав (подивіться на рис) жодної з клавіш:

{ | } ~ 

Трифографи були визначені для вирішення конкретної проблеми. Ідея полягала в тому, що програми C можуть використовувати підмножину ASCII, знайдену на ASR-33, а в інших середовищах відсутні високі значення ASCII.

Ваш приклад насправді є двома з них ??!, кожне значення |, тому результат є ||.

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

Це впевнено працювало, це призвело до дико популярного SO питання.

ASR-33 Teletype

ASR-33 Teletype


1. Що стосується цього, тригери були винайдені комітетом ANSI, який вперше зустрічався після C став успішним втечами, тому жоден з оригінальних C-кодів або кодерів не використовував би їх.


355
2017-10-19 21:09



Це не єдиний випадок відсутності символів, на клавіатурі та набору символів. Commodore 64, швидше за все, буде більш знайомий багатьом людям наприкінці тридцятих років і вище - на екрані наборів символів бракувало фігурних дужок (і, мабуть, і панелі та тильди) - у цьому випадку, оскільки "ASCII" не був ASCII . У ECMA-6 (майже завжди називають ASCII, але не US-ASCII) існувало 18 регіональних кодів, але я не знаю, які коди вони були. Єдина річ, яку я можу точно сказати - у британській "ASCII" #був заміщений на £. В інших регіонах, можливо, "ASCII" не мав жодних дужок тощо. - Steve314
Такий же набір символів ATASCII для 8-розрядних комп'ютерів Atari також не мав {}, а також ~ та `. - dan04
Побачити ці  два Статті Вікіпедії. Я вже досить старий, щоб ще пам'ятати епоху 7-бітних національних кодів (хоча я впевнений, що вони все ще затримуються в деяких темних незвернутих кутах), і книга, яку я вперше дізналася з C, вважає за потрібне попередити про можливість if (x || y) { a[i] = '\0'; } як виглядає if (x öö y) ä aÄiÅ = 'Ö0'; å в неправильному кодуванні. - Ilmari Karonen
Ще однією цікавою історичною заміткою є те, що Unix (яка була великою платформою C, що входить до системи), можливо, була першою системою будь-якого значення (і, можливо, першим загальним) за замовчуванням для алфавітних значень для нижнього регістру, а не для верхнього регістру. Хоча я не бачив на власні очі багато сучасних систем, я думаю, що це справжній знак витонченості. Крім того, що насправді єдина пристойна ОС, Unix також перетворив ваш верхній регістр на нижчу, а не навпаки. Ці хлопці були справді класні. - DigitalRoss
Смішна історія, я повинен сказати, що ... Компілятор XL Fortran IBM RS / 6000 був розроблений з компілятора XL C. У перших кількох релізах вони випадково вийшли в обробку триггер, тому існували деякі закодовані символьні послідовності Fortran (в буквальному рядку, IIRC), які були неправильно витлумачені як триграф C, що призвело до деяких цікавих помилок! - Phil Perry


Це C трифрагма. ??! є |, так ??!??! є оператором ||


140
2017-10-19 16:58



чому хтось повинен користуватися ??! замість | ??? / - Fatemeh Karimi
Триггер з'являється в період, коли деяка клавіатура не має всіх ключів, які вони мають зараз. Це також допомагає, коли якийсь текстовий редактор закріплює спеціальні символи для спеціальних речей. Це в основному реліквія минулого і майстер вікторини;) - Joel Falcou


Як вже зазначалося ??!??! по суті два тригери (??! і ??! знову) співані разом, які отримують замінити-переведені на ||, тобто Логічно АБО, препроцесором.

Наступне зображення, що містить усі тригери, повинно допомогти неоднозначно використовувати альтернативні комбінації трифрагментів:

enter image description here (Зображення взято з C: Довідник 5-го видання)

Отже, трифрагмент, який виглядає так ??(??) врешті-решт карта до [], ??(??)??(??) буде замінено на [][] і так далі, ви отримуєте ідею.

Оскільки трифраги замінюються під час попередньої обробки, ви можете використовувати cpp щоб отримати вигляд виходу самостійно, використовуючи дурний trigr.c програма:

void main(){ const char *s = "??!??!"; } 

і обробляючи його:

cpp -trigraphs trigr.c 

Ви отримаєте вихід консолі від

void main(){ const char *s = "||"; }

Як ви можете помітити, опція -trigraphs повинен бути вказаний чи інший cpp видає попередження; це вказує як Тригери є минулим минулим і не мають ніякої сучасної цінності, окрім заплутування людей, які можуть зіткнутися з ними.


Що стосується обгрунтування впровадження триграфів, то це краще зрозуміло, дивлячись на Історія розділ ISO/IEC 646:

ISO / IEC 646 та його попередник ASCII (ANSI X3.4) багато в чому схвалили існуючу практику щодо кодування символів у галузі телекомунікацій.

Оскільки ASCII не надав ряд символів, необхідних для інших мов, крім англійської, було зроблено ряд національних варіантів, які замінили деякі менш використані символи на необхідні


80
2018-03-25 02:24



+ 1 для таблиці інших триграфів. - Hi-Angel