อะไร ??!??! ผู้ประกอบการทำใน C?


1990

ฉันเห็นสาย C ที่มีลักษณะเช่นนี้:

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

มันรวบรวมอย่างถูกต้องและดูเหมือนว่าจะทำงานได้ ดูเหมือนว่ามันกำลังตรวจสอบว่ามีข้อผิดพลาดเกิดขึ้นหรือไม่และถ้ามีมันจะจัดการกับมัน แต่ฉันไม่แน่ใจจริงๆว่ามันกำลังทำอะไรอยู่หรือมันกำลังทำอะไรอยู่ ดูเหมือนว่าโปรแกรมเมอร์กำลังพยายามแสดงความรู้สึกเกี่ยวกับข้อผิดพลาด

ฉันไม่เคยเห็น??!??!มาก่อนในภาษาการเขียนโปรแกรมใด ๆ และฉันไม่สามารถหาเอกสารได้ทุกที่ (Google ไม่ได้ช่วยในเรื่องคำค้นหา??!??!) มันทำอะไรและตัวอย่างของโค้ดทำงานอย่างไร


44
@ PeterOlson คุณคาดหวังว่า!ErrorHasOccurred() ??!???! HandleError();จะรวบรวมได้อย่างไร ??! ??? !ที่ พิสูจน์จุดได้หรือไม่
CVn

31
ฉันขอแนะนำให้คุณอ่านรหัสสะอาด ErrorHasOccured () ควรได้รับการ refactored เป็น ErrorHasNotOccured () จึงล้างเครื่องหมายอัศเจรีย์ ... ที่มีเวลาทำความเข้าใจผู้ประกอบการเหล่านี้ทั้งหมด!
KadekM

17
ฉันชอบErrorHasOccured() && HandleError()ตัวเองมากกว่า นั่นเป็นวิธีที่ Lua ทำเช่นกัน
Hugo Zink

76
@ KadekM การเคลื่อนย้ายการปฏิเสธไปยังชื่อฟังก์ชั่นนั้นไม่ได้สร้างขึ้นเพื่อทำความสะอาดโค้ด แต่จะตรงกันข้าม
marcelm

14
หมายเหตุสำหรับใครก็ตามที่ลงเอยที่นี่หลังจากต่อสู้เพื่อความตายด้วยเครื่องมือค้นหาของพวกเขาSymbolHoundสามารถช่วยค้นหาสัญลักษณ์ได้
Jakob

คำตอบ:


1579

??!เป็นtrigraph|ที่แปลว่า ดังนั้นมันจึงพูดว่า:

!ErrorHasOccured() || HandleError();

ซึ่งเนื่องจากการลัดวงจรเทียบเท่ากับ:

if (ErrorHasOccured())
    HandleError();

ปราชญ์ประจำสัปดาห์ (จัดการกับ C ++ แต่เกี่ยวข้องกันที่นี่) ซึ่งฉันเลือกสิ่งนี้

ต้นกำเนิดที่เป็นไปได้ของ trigraphsหรือ @DwB ชี้ให้เห็นในความคิดเห็นที่เป็นไปได้มากขึ้นเนื่องจาก EBCDIC เป็นเรื่องยาก (อีกครั้ง) นี้การอภิปรายบนกระดาน IBM developerWorks ดูเหมือนจะสนับสนุนทฤษฎีที่ว่า

จาก ISO / IEC 9899: 1999 §5.2.1.1, เชิงอรรถ 12 (h / t @ Random832):

ลำดับ Trigraph เปิดใช้งานการป้อนข้อมูลของตัวละครที่ไม่ได้กำหนดไว้ในชุดรหัส Invariant ตามที่อธิบายไว้ใน ISO / IEC 646 ซึ่งเป็นชุดย่อยของชุดรหัส ASCII สหรัฐอเมริกาเจ็ดบิต


378
ตอนแรกจำเป็นต้องใช้ Trigraphs ในกรณีที่แป้นพิมพ์ของคุณไม่มีเช่น '|' สัญลักษณ์. นี่เป็นทั้งโปรแกรมเมอร์ที่ตั้งใจสร้างความรำคาญหรือ 'ฟีเจอร์' บรรณาธิการแปลก ๆ
Martin Beckett

36
if (ErrorHasOccured()) HandleError()ใช่มันเทียบเท่ากับ โชคดีที่คุณมักจะพบสำนวนนี้ในรหัส Perl เท่านั้น
user786653

22
มันไม่จำเป็นว่าจะต้องเป็น EBCDIC - ชุดของตัวละครที่ต้องการ trigraphs เกือบจะตรงกับชุดของตัวละครที่ไม่คงที่ใน ISO-646 (เช่นมาตรฐาน 'ascii แห่งชาติ' เก่า)
Random832

52
ทางเลือกที่อ่านได้อย่างสมบูรณ์แบบก็ErrorHasOccurred() && HandleError();คือถ้าคุณคุ้นเคยกับการเขียนสคริปต์เชลล์ :)
Yam Marcovic

18
อ่านเป็น "ทั้งไม่มีข้อผิดพลาดเกิดขึ้นหรือคุณต้อง HandleError", @SparkyRobinson
Omar Antolín-Camarena

453

เหตุใดเรื่องนี้จึงมีอยู่ทั่วไปอาจแตกต่างจากสาเหตุที่มีอยู่ในตัวอย่าง

ทุกอย่างเริ่มต้นเมื่อครึ่งศตวรรษก่อนด้วยการนำเสนอเทอร์มินัลการสื่อสารแบบสำเนาถาวรเป็นส่วนต่อประสานกับผู้ใช้คอมพิวเตอร์ ในยุคเริ่มต้นของ Unix และ C นั่นคือ ASR-33 Teletype

อุปกรณ์นี้ช้า (10 cps) และมีเสียงดังและน่าเกลียดและมุมมองของชุดอักขระ ASCII สิ้นสุดที่ 0x5f ดังนั้นจึงมี (ดูที่รูปอย่างใกล้ชิด) ไม่มีกุญแจ:

{ | } ~ 

ตรีโกณมิติถูกกำหนดเพื่อแก้ไขปัญหาเฉพาะ แนวคิดคือโปรแกรม C สามารถใช้ชุดย่อย ASCII ที่พบใน ASR-33 และในสภาพแวดล้อมอื่น ๆ ที่ไม่มีค่า ASCII สูง

ตัวอย่างของคุณเป็นจริงสองของ??!แต่ละความหมายดังนั้นผลที่ได้คือ|||

อย่างไรก็ตามคนที่เขียนรหัส C เกือบตามคำนิยามมีอุปกรณ์ที่ทันสมัย1ดังนั้นฉันเดาว่า: ใครบางคนกำลังแสดงออกหรือสร้างความสนุกให้ตัวเองทิ้งไข่อีสเตอร์ไว้ในรหัสเพื่อให้คุณค้นหา

มันใช้งานได้จริงมันนำไปสู่คำถาม SO ที่ได้รับความนิยมอย่างมาก

ASR-33 โทรพิมพ์

                                            ASR-33 โทรพิมพ์


1. สำหรับเรื่องนั้น Trigraphs ถูกคิดค้นโดยคณะกรรมการ ANSI ซึ่งพบกันครั้งแรกหลังจาก C กลายเป็นความสำเร็จที่หลบหนีดังนั้นจึงไม่มีรหัส C หรือตัวเขียนต้นฉบับดั้งเดิมที่จะใช้มัน


18
นี่ไม่ใช่กรณีเดียวของตัวละครที่หายไปในแป้นพิมพ์และชุดอักขระ พลเรือจัตวา 64 มีแนวโน้มที่จะคุ้นเคยกับผู้คนจำนวนมากในวัยสามสิบต้น ๆ ของพวกเขา - ตัวละครที่ปรากฏขึ้นทำให้ทั้งคู่ขาดเครื่องหมายปีกกา (และอาจเป็นแถบและตัวหนอน) - ในกรณีนี้เพราะ "ASCII" ไม่ใช่ ASCII . ใน ECMA-6 (เกือบจะเรียกว่า ASCII แต่ไม่ใช่ US-ASCII) มี 18 รหัสเฉพาะภูมิภาค แต่ฉันไม่รู้ว่าเป็นรหัสใด สิ่งหนึ่งที่ฉันสามารถพูดได้อย่างแน่นอน - ในอังกฤษ "ASCII" ถูกแทนที่ด้วย# £ในภูมิภาคอื่น ๆ อาจ "ASCII" ไม่มีเครื่องหมายวงเล็บ ฯลฯ
Steve314

7
อักขระ ATASCII ที่คล้ายกันที่ตั้งค่าสำหรับคอมพิวเตอร์ 8 บิตของ Atari ก็ขาด {} เช่นเดียวกับ ~ และ `
dan04

42
ดูเหล่านี้ สองบทความวิกิพีเดีย ฉันอายุมากพอที่จะยังคงจดจำยุคของตัวละครชาติ 7 บิต (แม้ว่าฉันแน่ใจว่าพวกเขายังคงอยู่ในมุมมืด ๆ ที่ไม่มั่นคง) และหนังสือที่ฉันเรียนรู้ครั้งแรก C พบว่าจำเป็นต้องเตือนเกี่ยวกับ ความเป็นไปได้ของการif (x || y) { a[i] = '\0'; }มองเช่นif (x öö y) ä aÄiÅ = 'Ö0'; åในชุดอักขระที่ไม่ถูกต้อง
Ilmari Karonen

9
บันทึกทางประวัติศาสตร์ที่น่าสนใจอีกอย่างหนึ่งคือ Unix (ซึ่งเป็นแพลตฟอร์มขนาดใหญ่ที่ใช้งานบน C) อาจเป็นระบบแรกที่มีความสำคัญ (และอาจรวมเป็นครั้งแรก) เป็นค่าตัวอักษรเริ่มต้นเป็นตัวพิมพ์เล็กแทนที่จะเป็นตัวพิมพ์ใหญ่ แม้ว่าฉันจะไม่เห็นด้วยตาของตัวเองในหลาย ๆ ระบบร่วมสมัย แต่ฉันคิดว่านี่เป็นสัญญาณที่บ่งบอกถึงความซับซ้อนอย่างแท้จริง นอกเหนือจากการเป็นระบบปฏิบัติการที่ดีเท่านั้น Unix ยังแปลงตัวพิมพ์ใหญ่ของคุณให้ต่ำลงแทนที่จะกลับกัน พวกนั้นเท่ห์จริงๆ
DigitalRoss

16
เรื่องตลกที่ต้องเล่าให้ฟัง ... คอมไพเลอร์ XL Fortran คอมไพเลอร์ของ IBM RS / 6000 ได้รับการพัฒนาจากคอมไพเลอร์ XL C ในการเผยแพร่ครั้งแรกพวกเขาทิ้งไว้โดยไม่ตั้งใจในการประมวลผลของ trigraph ดังนั้นจึงมีลำดับอักขระ Fortran ที่ถูกต้อง (ในสตริงตัวอักษร IIRC) ที่ตีความผิดในรูปแบบ C trigraph ซึ่งนำไปสู่ข้อบกพร่องที่น่าสนใจ!
Phil Perry

166

มันเป็น C trigraph ??!คือ|ดังนั้น??!??!ผู้ประกอบการ||


5
trigraph มาจากช่วงเวลาที่คีย์บอร์ดบางรุ่นไม่มีคีย์ทั้งหมดที่มีอยู่ตอนนี้ มันยังช่วยเมื่อตัวแก้ไขข้อความบางตัวสงวนอักขระพิเศษไว้สำหรับสิ่งพิเศษ ส่วนใหญ่เป็นของที่ระลึกของอดีตและ enabler quizz;)
Joel Falcou

5
เนื่องจากแป้นพิมพ์บางตัวไม่มี "|" ดังนั้นบางคนจึงไม่มีทางเลือกอื่นนอกจากใส่ปุ่มบนคีย์บอร์ดซ้ำ ๆ จนกระทั่งเกิด Trigraph ที่ให้สัญลักษณ์ที่ต้องการ
Owl

แล้วมี<iso646.h>ไฟล์ส่วนหัว
David R Tribble

149

ตามที่ระบุไว้แล้ว??!??!เป็นหลักสองtrigraphs ( ??!และ??!อีกครั้ง) ข้าวต้มด้วยกันที่ได้รับการแปล||แทนคือตรรกะหรือโดย preprocessor

ตารางต่อไปนี้ที่มี trigraph ทุกอันควรช่วยลดความซับซ้อนของชุด trigraph สำรอง:

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

แหล่งที่มา: C: คู่มืออ้างอิงรุ่นที่ 5

ดังนั้น trigraph ที่ดูเหมือนว่า??(??)ในที่สุดจะแมปไป[], ??(??)??(??)จะได้รับการแทนที่ด้วย[][]และเพื่อที่คุณจะได้รับความคิด

เนื่องจาก Trigraphs ถูกทดแทนในระหว่างการประมวลผลล่วงหน้าคุณสามารถใช้cppเพื่อรับมุมมองของผลลัพธ์ด้วยตัวคุณเองโดยใช้trigr.cโปรแกรมโง่ ๆ:

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

และประมวลผลด้วย:

cpp -trigraphs trigr.c 

คุณจะได้รับเอาต์พุตคอนโซลของ

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

ในขณะที่คุณสามารถสังเกตเห็นตัวเลือก-trigraphsจะต้องระบุมิฉะนั้นcppจะออกคำเตือน; นี้แสดงว่าtrigraphs เป็นสิ่งที่ผ่านมาและไม่มีค่าที่ทันสมัยอื่น ๆ กว่าสับสนคนที่อาจชนเข้ากับพวกเขา


สำหรับเหตุผลเบื้องหลังการแนะนำของตรีโกณมิตินั้นเป็นที่เข้าใจกันดีกว่าเมื่อดูที่ส่วนประวัติของ ISO / IEC 646 :

ISO / IEC 646 และ ASCII รุ่นก่อนหน้า (ANSI X3.4) ส่วนใหญ่รับรองการปฏิบัติที่มีอยู่เกี่ยวกับการเข้ารหัสอักขระในอุตสาหกรรมโทรคมนาคม

ในฐานะที่เป็น ASCII ไม่ได้ให้จำนวนตัวอักษรที่จำเป็นสำหรับภาษาอื่นที่ไม่ใช่ภาษาอังกฤษจำนวนของสายพันธุ์ของชาติที่ถูกสร้างขึ้นที่ทดแทนบางตัวอักษรน้อยกว่าที่ใช้กับคนที่จำเป็น

(เน้นที่เหมือง)

ดังนั้นในสาระสำคัญอักขระบางตัวที่ต้องการ (ที่มีอยู่ใน trigraph) จึงถูกแทนที่ด้วยตัวแปรระดับชาติบางอย่าง สิ่งนี้นำไปสู่การเป็นตัวแทนสำรองโดยใช้ trigraph ซึ่งประกอบด้วยอักขระที่ตัวแปรอื่นยังมีอยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.