C'deki >>> = operatörü nedir?


294

Bir meslektaşım tarafından bir bulmaca olarak verildiğinde, bu C programının aslında nasıl derlendiğini ve çalıştığını anlayamıyorum. Bu >>>=operatör ve garip 1P1gerçek nedir? Clang ve GCC'de test yaptım. Hiçbir uyarı yok ve çıktı "???"

#include <stdio.h>

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

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

    return 0;
}

36
Bunlardan bazıları digraf .
juanchopanza

12
@Kay, bu durumda hayır::> =] sonra bir [...] >> = a [...]
Adriano Repetti

6
@Marc Ben ">>> =" olabileceğini sanmıyorum çünkü bu derlemek olmaz, ancak yukarıdaki kod aslında derler.
CustomCalc

21
0x.1P1Üstelinin onaltılık değişmezi olduğunu. Bu 0x.1sayı kısmı veya 1/16 burada. 'P' den sonraki sayı, sayının çarpıldığı ikinin gücüdür. Yani 0x.1p1gerçekten 1/16 * 2 veya 1/8. Ve merak olsaydı 0xFULLbu sadece var 0xFve ULLbir için soneklerdirunsigned long long
jackarms

71
C sözdizimi - pundits ve trivia sevenler için sonsuz malzeme, ama sonuçta hepsi bu kadar önemli değil.
Kerrek SB

Yanıtlar:


468

Çizgi:

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

içeren digraphs :> ve <:çevirmek, ]ve [bu yüzden eşdeğer olduğunu, sırasıyla:

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

Değişmez 0xFULLdeğer 0xF(ile onaltılık olan 15) ile aynıdır ; ULLbu sadece belirtir bir var unsigned long longdeğişmezi . Böylece herhangi bir durumda, boolean olarak bu doğrudur 0xFULL ? '\0' : -1değerlendirir için '\0'bir olan karakter değişmezi basitçe kimin sayısal değer 0.

Bu arada, 0X.1P1a, değişmez onaltılık kayan nokta 2/16 = 0.125 oranları ile aynıdır. Her durumda, sıfırdan farklı olarak, bir boolean olarak da doğrudur, bu yüzden !!tekrar iki kez reddetmek üretir 1. Böylece, her şey basitleşir:

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

Operatör >>=, sol işlenenini sağ işlenen tarafından verilen bit sayısına göre bitiren ve sonucu döndüren bileşik bir atamadır . Bu durumda, sağ işlenen a[1]her zaman değere sahiptir 1, bu nedenle:

while( a[0] >>= 1 )

Veya eşdeğer olarak:

while( a[0] /= 2 )

Başlangıç ​​değeri a[0]10'dur. Sağa kaydırıldıktan sonra 5 olur, sonra (aşağı yuvarlanır) 2, sonra 1 ve son olarak 0 olur, bu noktada döngü sona erer. Böylece, döngü gövdesi üç kez yürütülür.


18
Eğer üzerinde durmak misiniz Pin 0X.1P1.
kay - SE kötülük

77
@Kay: Bu aynı şey ede 10e5kullanmak zorunda hariç pçünkü onaltılık sayılların eonaltılık rakamdır.
Dietrich Epp

9
@Kay: Hex float değişmez değerleri C99'un bir parçasıdır, ancak GCC bunları C ++ kodunda da kabul eder . Dietrich'in belirttiği gibi, pmantis ve üssü ayırır, tıpkı enormal bilimsel şamandıra gösterimlerinde olduğu gibi; bir fark, onaltılık kayan nokta ile üstel parçanın tabanının 10 yerine 2 olması, bu nedenle 0x0.1p10x0.1 = 1/16 çarpı 2¹ = 2'ye eşittir. (Her durumda, bunların hiçbiri burada önemli değildir; sıfır olmayan değer orada eşit derecede iyi çalışır.)
Ilmari Karonen

6
@chux: Görünüşe göre, bağlı kod C veya derlenmiş olup olmadığına C ++ (orijinal olarak etiketlendi gibi). Ancak metni "değişmez" yerine "karakter değişmez" diyecek şekilde düzelttim charve bir Wikipedia bağlantısı ekledim. Teşekkürler!
Ilmari Karonen

8
Güzel azaltma.
Corey

69

Bu içeren bazı oldukça belirsiz kod digraphs yani, <:ve :>alternatif belirteçleri olan [ve ]sırasıyla. Koşullu operatörün de bazı kullanımları vardır . Ayrıca biraz vites operatörü , sağ vardiya ataması da var >>=.

Bu daha okunabilir bir versiyon:

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

ve daha da okunabilir bir sürüm olarak, çözümledikleri []değerler için ifadeleri değiştirerek :

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

Değiştirmek a[0]ve a[1]değerleri için, döngünün ne yaptığını, yani aşağıdakilerin eşdeğerini bulmayı kolaylaştıracaktır:

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

bu, her bir yinelemede basitçe (tamsayı) bölünmeyi gerçekleştirerek diziyi üretir 5, 2, 1.


Ben çalıştırmadım - bu OP var ????yerine üretmek değil ???mi? (Ha). Codepad.org/nDkxGUNi yapar üretmek ???.
usr2564301

7
@ 10 yazılımı ilk yinelemeye ayrıldı. Bu yüzden döngü tarafından değerlendirilen değerler 5, 2, 1 ve 0'dır. Böylece sadece 3 kez yazdırılır.
MysticXG

42

Soldan sağa ifadesini inceleyelim:

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

Fark ettiğim ilk şey, üçlü operatör kullanmanın kullanılmasıdır ?. Yani alt ifade:

0xFULL ? '\0' : -1

eğer" söylediğini 0xFULLsıfırdan, dönüş olduğunu '\0'aksi takdirde -1. 0xFULLile onaltılık değişmezi olan işaretsiz uzun uzun sonek - bu tip bir onaltılık değişmezi olduğu anlamına unsigned long longçünkü, gerçekten önemli olsa değildir. 0xFDüzenli tamsayı içine sığabilir.

Ayrıca, üçlü operatör ikinci ve üçüncü terimlerin türlerini ortak türlerine dönüştürür. '\0'sonra dönüştürülür int, sadece 0.

Değeri 0xFsıfırdan çok daha büyük, bu yüzden geçiyor. İfade şimdi:

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

Sonra, :>bir digraf . Aşağıdakilere genişleyen bir yapıdır ]:

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

>>=imzalı sağ vardiya operatörü, adaha net hale getirmek için bunu boşluk bırakabiliriz .

Dahası, <:aşağıdakilere genişleyen bir digraftır [:

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

0X.1P1bir üssü olan onaltılık bir değişmez değerdir. Ama değer ne olursa olsun, !!sıfır olmayan herhangi bir şey doğrudur. 0X.1P1bir 0.125hale gelir, böylece sıfır olmayan, aşağıdakilerden biridir:

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

>>=İmzalı sağa kaydırma operatörüdür. Sol işleneninin değerini, operatörün sağ tarafındaki değerle ileri doğru kaydırarak değiştirir. 10ikili 1010. İşte adımlar:

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

>>=a[0], bitlerinin her bir sağa kaydırıldığı her defasında sıfırdan farklı kaldığı sürece, işlemin sonucunu döndürür, döngü devam eder. Dördüncü girişim nerede a[0]olur 0, bu yüzden döngü asla girilmez.

Sonuç olarak, ?üç kez yazdırılır.


3
:>bir digraf , bir trigraf değil. Önişlemci tarafından ele alınmaz, basitçe bir token eşdeğeri olarak tanınır ].
Keith Thompson

@KeithThompson Teşekkürler
0x499602D2

1
Üçlü işleç ( ?:), ikinci ve üçüncü terimlerin ortak türü olan bir türe sahiptir. İlk terim daima koşulludur ve bir türü vardır bool. Hem ikinci hem de üçüncü terimler tipe intsahip olduğundan üçlü işlemin sonucu intolmayacaktır unsigned long long.
Corey

2
@ KeithThompson önişlemci tarafından işlenebilir. Önişlemci çünkü digraphs bilmesi gerekmez #ve ##digraph formları var; erken çeviri aşamalarında digraph'ları digraph olmayanlara çevirmekten alıkoyan hiçbir şey yoktur
MM

@MattMcNabb Bunu bilmek zorunda olduğumdan beri uzun zaman geçti, ancak diğer şartların bir sonucu olarak IIRC, pp-tokenlerin jetonlara dönüştürüldüğü noktaya kadar (çeviri aşamasının başlangıcında) digraf formunda kalmalıdır. 7).
zwol
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.