ตัวดำเนินการ >>> = ใน C คืออะไร


294

ได้รับจากเพื่อนร่วมงานเป็นตัวต่อฉันไม่สามารถเข้าใจได้ว่าโปรแกรม C นี้รวบรวมและใช้งานได้อย่างไร >>>=ตัวดำเนินการนี้และ1P1ตัวอักษรแปลกคืออะไร ฉันได้ทำการทดสอบใน Clang และ GCC แล้ว ไม่มีคำเตือนและผลลัพธ์คือ "???"

#include <stdio.h>

int main()
{
    int a[2]={ 10, 1 };

    while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
        printf("?");

    return 0;
}

36
บางส่วนของผู้ที่มีdigraphs
juanchopanza

12
@Kay ไม่มีในกรณีนี้::> =] แล้ว [... ] >> = a [... ]
Adriano Repetti

6
@ Marc ฉันไม่คิดว่ามันจะเป็น ">>> =" เพราะมันไม่ได้รวบรวม แต่โค้ดข้างต้นคอมไพล์แล้ว
CustomCalc

21
นี่0x.1P1คือตัวอักษรฐานสิบหกที่มีเลขชี้กำลัง นี่0x.1คือส่วนตัวเลขหรือ 1/16 ที่นี่ จำนวนที่อยู่หลังตัว 'P' คือกำลังสองจำนวนที่คูณด้วย ดังนั้น0x.1p1มัน 1/16 * 2 หรือ 1/8 และถ้าคุณสงสัย0xFULLว่ามันเป็นเพียงแค่0xFนั้นและULLเป็นคำต่อท้ายสำหรับunsigned long long
jackarms

71
ไวยากรณ์ C - วัสดุที่ไม่มีที่สิ้นสุดสำหรับผู้ที่มีใจรักและผู้ที่มีความรู้รอบเรื่องเล็กน้อย แต่ท้ายที่สุดไม่ได้มีความสำคัญทั้งหมด
Kerrek SB

คำตอบ:


468

เส้น:

while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )

มีdigraphs :>และ<:ซึ่งแปล]และ[ตามลำดับดังนั้นจึงเท่ากับ:

while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )

ตัวอักษร0xFULLเหมือนกับ0xF(ซึ่งเป็นฐานสิบหก15); ULLเพียงระบุว่ามันเป็นunsigned long longตัวอักษร ในกรณีใด ๆ เป็นแบบบูลมันเป็นความจริงดังนั้น0xFULL ? '\0' : -1ประเมิน'\0'ซึ่งเป็นตัวอักษรตัวละคร0ที่มีค่าเป็นเพียงตัวเลข

ในขณะเดียวกัน0X.1P1ก็คือจุดลอยตัวฐานสิบหกตามตัวอักษรเท่ากับ 2/16 = 0.125 ในกรณีใด ๆ เป็นที่ไม่ใช่ศูนย์ก็ยังเป็นความจริงเป็นแบบบูลเพื่อกวนเป็นครั้งที่สองกับอีกผลิต!! 1ดังนั้นสิ่งทั้งหมดลดความซับซ้อนลงไปที่:

while( a[0] >>= a[1] )

ผู้ประกอบการ>>=คือการกำหนดผสมที่บิตเลื่อนตัวถูกดำเนินการทางซ้ายขวาตามจำนวนบิตที่ได้รับจากตัวถูกดำเนินการที่เหมาะสมและส่งกลับผล ในกรณีนี้ตัวถูกดำเนินการด้านขวาa[1]จะมีค่าอยู่เสมอ1ดังนั้นจึงเท่ากับ:

while( a[0] >>= 1 )

หรือเทียบเท่า:

while( a[0] /= 2 )

ค่าเริ่มต้นa[0]คือ 10 หลังจากเลื่อนไปทางขวาหนึ่งครั้งมันจะกลายเป็น 5 จากนั้น (ปัดเศษลง) 2 จากนั้น 1 และสุดท้าย 0 ซึ่งจุดที่ลูปสิ้นสุด ดังนั้นร่างกายห่วงได้รับการดำเนินการสามครั้ง


18
คุณช่วยกรุณาอธิบายรายละเอียดเกี่ยวกับในP 0X.1P1
kay - SE ชั่ว

77
@Kay: มันเหมือนกับeใน10e5ยกเว้นคุณจะต้องใช้pสำหรับตัวอักษรฐานสิบหกเพราะeเป็นเลขฐานสิบหก
Dietrich Epp

9
@Kay: ตัวอักษรเลขฐานสิบหกแบบลอยเป็นส่วนหนึ่งของ C99 แต่GCC ก็ยอมรับรหัส C ++ด้วย ในฐานะที่เป็นทริชบันทึกย่อpแยกแมนทิสซาและเลขชี้กำลังเช่นเดียวกับeสัญกรณ์ลอยทางวิทยาศาสตร์ปกติ; ความแตกต่างอย่างหนึ่งคือด้วยฐานหกเหลี่ยมฐานของส่วนเอ็กซ์0x0.1p1โปเนนเชียลคือ 2 แทน 10 ดังนั้นเท่ากับ 0x0.1 = 1/16 คูณ 2 (= 2 (ในกรณีใด ๆ ไม่มีประเด็นใดที่นี่ไม่มีค่าศูนย์ใด ๆ คุณค่าจะใช้ได้ดีพอ ๆ กัน)
Ilmari Karonen

6
@chux: เห็นได้ชัดว่าขึ้นอยู่กับว่ารวบรวมรหัสเป็น C หรือ (ตามที่ถูกติดแท็ก) C ++ แต่ฉันแก้ไขข้อความเพื่อพูดว่า "ตัวอักษรตัวอักษร" แทนที่จะเป็น " charตัวอักษร" และเพิ่มลิงค์ Wikipedia ขอบคุณ!
Ilmari Karonen

8
การลดที่ดี
Corey

69

มันเป็นรหัสที่ค่อนข้างคลุมเครือเกี่ยวกับdigraphsคือ<:และ:>ซึ่งเป็นโทเค็นทางเลือกสำหรับ[และ]ตามลำดับ นอกจากนี้ยังมีการใช้งานของบางผู้ประกอบการที่มีเงื่อนไข นอกจากนี้ยังมีผู้ประกอบการบิตขยับ>>=ที่ได้รับมอบหมายกะขวา

นี่เป็นรุ่นที่อ่านได้มากขึ้น:

while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )

และรุ่นที่อ่านได้มากขึ้นโดยแทนที่นิพจน์ใน[]สำหรับค่าที่แก้ไขไป:

while( a[0] >>= a[1] )

การเปลี่ยนa[0]และa[1]ค่าของมันควรทำให้ง่ายต่อการเข้าใจว่าการวนซ้ำกำลังทำอะไรอยู่นั่นเท่ากับ:

int i = 10;
while( i >>= 1)

ซึ่งเป็นเพียงการดำเนินการ (จำนวนเต็ม) หารด้วย 2 ในแต่ละซ้ำ, 5, 2, 1การผลิตลำดับ


ฉันไม่ได้ใช้มัน - นี่จะไม่ได้ผล????มากกว่า???ที่ OP ได้หรือไม่ (อืมมม.) codepad.org/nDkxGUNi ไม่???ผลิต
usr2564301

7
@Jongware ทั้ง 10 ได้ถูกแบ่งในการทำซ้ำครั้งแรก ดังนั้นค่าที่ประเมินโดยลูปคือ 5, 2, 1 และ 0 ดังนั้นมันจึงพิมพ์ออกมา 3 ครั้งเท่านั้น
MysticXG

42

ลองทำนิพจน์จากซ้ายไปขวา:

a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]

?แจ้งให้ทราบล่วงหน้าสิ่งแรกที่ผมก็คือว่าเราจะใช้ผู้ประกอบการที่ประกอบไปด้วยจากการใช้ subexpression ดังนั้น:

0xFULL ? '\0' : -1

ไม่ว่าจะเป็น "ถ้า0xFULLเป็นที่ไม่ใช่ศูนย์กลับ'\0'มิฉะนั้น-1. 0xFULLเป็นตัวอักษรเลขฐานสิบหกกับไม่ได้ลงนามต่อท้ายยาวยาว - ความหมายมันเป็นตัวอักษรเลขฐานสิบหกชนิดunsigned long longที่ไม่ได้เรื่องจริงๆ แต่เนื่องจาก. 0xFสามารถพอดีภายในจำนวนเต็มปกติ

นอกจากนี้ผู้ประกอบการที่ประกอบไปด้วยสามแปลงประเภทของคำที่สองและสามเป็นประเภทที่พบบ่อย '\0'จะถูกแปลงไปแล้วซึ่งเป็นเพียงint0

มูลค่าของ0xFมันใหญ่กว่าศูนย์ดังนั้นมันจึงผ่านไป ตอนนี้การแสดงออกกลายเป็น:

a[ 0 :>>>=a<:!!0X.1P1 ]

ถัดไป:>เป็นเดี่ยว มันเป็นโครงสร้างที่ขยายไปถึง]:

a[0 ]>>=a<:!!0X.1P1 ]

>>=เป็นผู้ดำเนินการกะด้านขวาที่ลงนามแล้วเราสามารถเว้นที่ว่างaเพื่อให้ชัดเจนยิ่งขึ้น

ยิ่งไปกว่านั้น<:คือ digraph ที่ขยายไปยัง[:

a[0] >>= a[!!0X.1P1 ]

0X.1P1เป็นตัวอักษรฐานสิบหกที่มีเลขชี้กำลัง แต่ไม่ว่าคุณค่า!!ของสิ่งใดก็ตามที่ไม่ใช่ศูนย์จะเป็นจริง 0X.1P1เป็น0.125ที่เป็นที่ไม่ใช่ศูนย์จึงกลายเป็น:

a[0] >>= a[true]
-> a[0] >>= a[1]

นี่>>=คือโอเปอเรเตอร์กะขวาที่เซ็นชื่อ มันเปลี่ยนค่าของตัวถูกดำเนินการด้านซ้ายโดยเลื่อนบิตไปข้างหน้าด้วยค่าทางด้านขวาของตัวดำเนินการ ในไบนารีคือ10 1010ดังนั้นนี่คือขั้นตอน:

01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000

>>=ส่งคืนผลลัพธ์ของการดำเนินการดังนั้นตราบใดที่การเลื่อนa[0]ยังคงไม่เป็นศูนย์สำหรับทุกครั้งที่บิตถูกเลื่อนไปทางขวาหนึ่งวงจะดำเนินการต่อ ความพยายามครั้งที่สี่คือที่ที่a[0]จะ0เกิดการวนซ้ำดังนั้นจึงไม่เคยเข้ามา

เป็นผลให้?พิมพ์สามครั้ง


3
:>เป็นdigraphไม่ใช่ trigraph มันไม่ได้รับการจัดการโดย preprocessor ]ก็จำได้เพียงเป็นเทียบเท่าโทเค็น
Keith Thompson

@KeithThompson ขอบคุณ
0x499602D2

1
ผู้ประกอบการที่ประกอบไปด้วย ( ?:) มีประเภทที่เป็นประเภททั่วไปของคำที่สองและสาม เทอมแรกเป็นเงื่อนไขและมีประเภทboolเสมอ เนื่องจากคำศัพท์ที่สองและที่สามมีประเภทintผลลัพธ์ของการดำเนินการแบบไตรภาคจะintไม่เป็นunsigned long longเช่นนั้น
Corey

2
@ KeithThompson มันสามารถจัดการได้โดย preprocessor ตัวประมวลผลล่วงหน้าต้องรู้เกี่ยวกับ digraphs เพราะ#และ##มีรูปแบบของ digraph; ไม่มีอะไรที่จะหยุดการนำไปใช้งานตั้งแต่การแปล digraphs ไปเป็น non-digraphs ในช่วงการแปลขั้นต้น
MM

@MattMcNabb เป็นเวลานานแล้วที่ฉันต้องรู้เรื่องนี้ แต่ IIRC เป็นผลมาจากข้อกำหนดอื่น ๆ digraphs ต้องอยู่ในรูปแบบของ digraph จนกว่าจะถึงจุดที่ pp-tokens ถูกแปลงเป็นโทเค็น (ขวาที่จุดเริ่มต้นของขั้นตอนการแปล 7)
zwol
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.