web-dev-qa-db-ja.com

Cの>>> =演算子とは何ですか?

同僚からパズルとして与えられたものですが、この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;
}
290
CustomCalc

この線:

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

digraphs:>および<:を含み、それぞれ]および[に変換されるため、以下と同等です。

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

リテラル0xFULLは、0xF15の16進数)と同じです。 ULLは、単に unsigned long longリテラル と指定するだけです。いずれにせよ、ブール値としては真なので、0xFULL ? '\0' : -1'\0'に評価されます。これは、数値が単に0である 文字リテラル です。

一方、0X.1P116進浮動小数点リテラル 2/16 = 0.125に等しいです。いずれにせよ、ゼロ以外であり、ブール値としても真であるため、!!で2回否定すると、再び1が生成されます。したがって、全体が次のように単純化されます。

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

演算子>>=複合割り当て で、右のオペランドで指定されたビット数だけ左のオペランドを右にビットシフトし、結果を返します。この場合、右側のオペランドa[1]の値は常に1であるため、次と同等です。

while( a[0] >>= 1 )

または、同等に:

while( a[0] /= 2 )

a[0]の初期値は10です。右に1回シフトした後、5になり(切り捨て)、2、1、最後に0になり、ループが終了します。したがって、ループ本体は3回実行されます。

463
Ilmari Karonen

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を生成します。

67
juanchopanza

式を左から右に見ていきましょう。

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

最初に気づいたのは、?の使用から三項演算子を使用していることです。したがって、部分式:

0xFULL ? '\0' : -1

0xFULLがゼロ以外の場合、'\0'を返し、そうでない場合は-1を返します。0xFULLは、unsigned longを持つ16進リテラルです-long suffix-これはunsigned long long型の16進リテラルであることを意味しますが、0xFは通常の整数に収まるため、実際には問題になりません。

また、三項演算子は、2番目と3番目の項のタイプを共通タイプに変換します。 '\0'intに変換されます。これは0です。

0xFの値はゼロよりもはるかに大きいため、合格します。式は次のようになります。

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

次に、:>ダイグラフ です。これは、]に展開される構成体です。

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

>>=は符号付き右シフト演算子です。それをaから外して、明確にすることができます。

さらに、<:[に展開される有向グラフです:

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

0X.1P1は、指数を持つ16進リテラルです。ただし、値に関係なく、ゼロ以外のすべての!!はtrueです。 0X.1P10.125であり、ゼロではないため、次のようになります。

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

>>=は、符号付き右シフト演算子です。演算子の右側の値でビットを前方にシフトすることにより、左側のオペランドの値を変更します。 10はバイナリで1010です。手順は次のとおりです。

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

>>=は演算の結果を返します。そのため、ビットが1つ右にシフトされるたびにa[0]がゼロ以外のままである限り、ループは継続します。 4番目の試みは、a[0]0になる場所であるため、ループに入ることはありません。

その結果、?が3回出力されます。

41
0x499602D2