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
그들 중 일부는 digraph 입니다.
juanchopanza

12
@Kay,이 경우에 아니오 : :> =] 그리고 a [...] >> = a [...]
Adriano Repetti 22:08

6
@Marc 컴파일되지 않을 것이기 때문에 ">>> ="일 수는 없다고 생각하지만, 위의 코드는 실제로 컴파일됩니다.
CustomCalc

21
0x.1P1지수와 16 진수 문자이다. 는 0x.1숫자 부분, 또는 여기에 1/16. 'P'뒤의 숫자는 2의 거듭 제곱입니다. 따라서 0x.1p1실제로 1/16 * 2 또는 1/8입니다. 그리고 당신에 대해 궁금해한다면 0xFULL그 것은 단지 0xFULL의 접미사입니다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은 (와 0xF16 진수 15) 와 동일합니다 . ULL것을 단지 지정 그것이이다 unsigned long long리터럴 . 그래서 어떤 경우에는, 부울로는, 사실 0xFULL ? '\0' : -1평가됩니다에 '\0'문자 리터럴 단순히 그 수치 0.

한편, 0X.1P1A는 리터럴 진수 부동 소수점 2/16 = 0.125 같음. 어쨌든 0이 아닌 경우도 부울로도 true이므로 !!다시 두 번 부정하면을 생성합니다 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있기 때문에 진수 리터럴에 대한 e16 진수 숫자입니다.
Dietrich Epp

9
@Kay : 16 진 부동 리터럴은 C99의 일부이지만 GCC는이를 C ++ 코드에서도 허용합니다 . Dietrich가 지적한 p것처럼, e일반 과학 플로트 표기법 과 마찬가지로 가수와 지수를 분리합니다 . 한 가지 차이점은 16 진 부동 소수점의 경우 지수 부분의 밑이 10이 아니라 2이므로 0x0.1p10x0.1 = 1/16 곱하기 2¹ = 2와 같습니다. 가치는 똑같이 잘 작동 할 것입니다.)
Ilmari Karonen

6
@chux : 분명히 코드가 C로 컴파일되는지 또는 (원래 태그가 지정된대로) C ++로 컴파일되는지에 따라 다릅니다 . 그러나 텍스트를 "리터럴"대신 "문자 리터럴"이라고 수정하고 charWikipedia 링크를 추가했습니다. 감사!
Ilmari Karonen

8
좋은 감소.
Corey

69

그것은 포함하는 다소 모호한 코드 이중 음자 즉, <::>대체 토큰입니다 []각각입니다. 의 일부 사용도 있습니다 조건 연산자 . 도있다 비트 이동 연산자 오른쪽 시프트 할당 >>=.

이것은 더 읽기 쉬운 버전입니다.

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가 얻은 ????것보다는 오히려 생산하지 ???않겠습니까? (Huh.) codepad.org/nDkxGUNi 는을 생성 ???합니다.
usr2564301

7
@Jongware 10은 첫 번째 반복으로 나뉘어졌습니다. 따라서 루프에 의해 평가되는 값은 5, 2, 1 및 0입니다. 따라서 3 회만 인쇄됩니다.
MysticXG

42

왼쪽에서 오른쪽으로 표현을 봅시다.

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

내가 주목하는 첫 번째는의 사용에서 삼항 연산자를 사용하고 있다는 것입니다 ?. 하위 표현식 :

0xFULL ? '\0' : -1

" 0xFULL0이 아닌 경우 return '\0', 그렇지 않으면 -1. 0xFULL부호없는 긴 접미사 가있는 16 진수 리터럴입니다 unsigned long long. 즉 , 형식의 16 진수 리터럴입니다 . 0xF일반 정수 안에 들어갈 수 있기 때문에 실제로 중요하지 않습니다 .

또한 삼항 연산자는 두 번째 및 세 번째 항의 유형을 공통 유형으로 변환합니다. '\0'다음으로 변환됩니다 int단지 인 0.

0xF이 0보다 크므로 통과합니다. 이제 표현은 다음과 같습니다.

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

다음 :>digraph 입니다. 다음으로 확장되는 구조입니다 ].

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

>>=부호가있는 오른쪽 시프트 연산자 a입니다. 더 명확하게하기 위해 간격을 둘 수 있습니다.

또한 다음과 같이 <:확장되는 digraph입니다 [.

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

0X.1P1지수가있는 16 진 리터럴입니다. 그러나 가치에 관계없이 !!0이 아닌 것은 사실입니다. 0X.1P10.125은가되도록 0이 아닌, 어느 :

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

>>=서명 오른쪽 시프트 연산자입니다. 연산자 오른쪽의 값만큼 비트를 앞으로 이동하여 왼쪽 피연산자의 값을 변경합니다. 10이진수로입니다 1010. 단계는 다음과 같습니다.

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

>>=a[0]비트가 1 씩 오른쪽으로 시프트 될 때마다 시프트 가 0이 아닌 한, 루프는 계속 작동하므로 연산 결과를 리턴합니다 . 네 번째 시도는 where a[0]0되어 루프가 입력되지 않습니다.

결과적으로 ?세 번 인쇄됩니다.


3
:>trigraph 가 아니라 digraph 입니다. 전처리기에 의해 처리되지 않으며 단순히에 해당하는 토큰으로 인식됩니다 ].
Keith Thompson

@KeithThompson 감사
0x499602D2

1
삼항 연산자 ( ?:)에는 두 번째 및 세 번째 항의 공통 유형 인 유형이 있습니다. 첫 번째 용어는 항상 조건부이며 유형이 bool있습니다. 두 번째와 세 번째 항은 모두 유형 int이 있기 때문에 삼항 연산의 결과는 int아닙니다 unsigned long long.
Corey

2
@KeithThompson 전처리기에 의해 처리 될 수 있습니다. 전처리 때문에 이중 음자에 대해 알고있다 ###소리를 나타내는 두 글자 형태를 가지고, 초기 번역 단계에서 구현 된 그래프를 비 그래프로 번역하는 것을 막을 수있는 것은 없습니다
MM

@MattMcNabb이 사실을 알고 지낸 지 오래되었지만 IIRC는 다른 요구 사항의 결과로 pp- 토큰이 토큰으로 변환되는 시점까지 (번역 단계가 시작될 때까지) digraph 형식을 유지해야합니다. 7).
zwol
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.