Toán tử >>> = trong C là gì?


294

Được đưa ra bởi một đồng nghiệp như một câu đố, tôi không thể hiểu làm thế nào chương trình C này thực sự biên dịch và chạy. >>>=Toán tử này và 1P1nghĩa đen là gì? Tôi đã thử nghiệm ở Clang và GCC. Không có cảnh báo và đầu ra là "???"

#include <stdio.h>

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

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

    return 0;
}

36
Một số đó là những chữ ghép .
juanchopanza

12
@Kay, không có trong trường hợp này ::> =] sau đó một [...] >> = a [...]
Adriano Repetti

6
@Marc Tôi không nghĩ nó có thể là ">>> =" vì điều đó sẽ không biên dịch, tuy nhiên đoạn mã trên thực sự biên dịch.
CustomCalc

21
Đây 0x.1P1là một chữ thập lục phân với số mũ. Đây 0x.1là phần số, hoặc 1/16 ở đây. Số sau 'P' là lũy thừa của hai số được nhân với. Vì vậy, 0x.1p1thực sự là 1/16 * 2, hoặc 1/8. Và nếu bạn đang tự hỏi về 0xFULLđiều đó chỉ 0xF, và ULLlà hậu tố cho mộtunsigned long long
jackarms

71
Cú pháp C - tài liệu vô tận cho những người yêu thích và những người yêu thích đố, nhưng cuối cùng không quan trọng lắm.
Kerrek SB

Câu trả lời:


468

Dòng:

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

chứa các bản vẽ :><:, dịch sang ][tương ứng, do đó, nó tương đương với:

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

Nghĩa đen 0xFULLgiống như 0xF(đó là hex cho 15); các ULLchỉ quy định cụ thể rằng nó là một unsigned long longnghĩa đen . Trong mọi trường hợp, với tư cách là một boolean, điều đó đúng, do đó, 0xFULL ? '\0' : -1đánh giá '\0', đó là một ký tự theo nghĩa đen có giá trị bằng số đơn giản 0.

Trong khi đó, 0X.1P1là một dấu phẩy động thập lục phân bằng chữ bằng 2/16 = 0,125. Trong mọi trường hợp, khác không, nó cũng đúng như một boolean, do đó phủ nhận nó hai lần với !!một lần nữa tạo ra 1. Do đó, toàn bộ điều đơn giản hóa thành:

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

Toán tử >>=là một phép gán tổng hợp thay đổi toán hạng bên trái của nó theo số bit được đưa ra bởi toán hạng bên phải và trả về kết quả. Trong trường hợp này, toán hạng bên phải a[1]luôn có giá trị 1, vì vậy nó tương đương với:

while( a[0] >>= 1 )

hoặc, tương đương:

while( a[0] /= 2 )

Giá trị ban đầu a[0]là 10. Sau khi dịch chuyển sang phải một lần, nó trở thành 5, sau đó (làm tròn xuống) 2, rồi 1 và cuối cùng là 0, tại đó vòng lặp kết thúc. Do đó, cơ thể vòng lặp được thực hiện ba lần.


18
Bạn có thể xin hãy giải thích trên Ptrong 0X.1P1.
kay - SE là ác

77
@Kay: Nó giống như etrong 10e5, ngoại trừ bạn phải sử dụng pcho các chữ thập lục phân vì elà một chữ số thập lục phân.
Dietrich Epp

9
@Kay: Chữ nổi hex là một phần của C99, nhưng GCC cũng chấp nhận chúng trong mã C ++ . Như Dietrich lưu ý, pphân tách lớp phủ và số mũ, giống như eký hiệu nổi khoa học thông thường; một điểm khác biệt là, với các hình nổi hex, cơ sở của phần mũ là 2 thay vì 10, do đó 0x0.1p1bằng 0x0.1 = 1/16 lần 2¹ = 2. (Trong mọi trường hợp, không có vấn đề nào ở đây; giá trị sẽ hoạt động tốt như nhau ở đó.)
Ilmari Karonen

6
@chux: Rõ ràng, điều đó phụ thuộc vào việc mã được biên dịch thành C hay (như ban đầu được gắn thẻ) C ++. Nhưng tôi đã sửa văn bản để nói "ký tự" thay vì " charnghĩa đen" và thêm một liên kết Wikipedia. Cảm ơn!
Ilmari Karonen

8
Giảm đẹp.
Corey

69

Đó là một số mã khá tối nghĩa liên quan đến các bản đồ , cụ thể <::>là các mã thông báo thay thế cho []tương ứng. Ngoài ra còn có một số sử dụng của toán tử điều kiện . Ngoài ra còn có một toán tử dịch chuyển bit , phân công dịch chuyển bên phải >>=.

Đây là một phiên bản dễ đọc hơn:

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

và một phiên bản thậm chí dễ đọc hơn, thay thế các biểu thức trong []các giá trị mà chúng giải quyết thành:

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

Thay thế a[0]a[1]cho các giá trị của chúng sẽ giúp bạn dễ dàng tìm ra vòng lặp đang làm gì, tức là tương đương với:

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

chỉ đơn giản là thực hiện phép chia (số nguyên) cho 2 trong mỗi lần lặp, tạo ra chuỗi 5, 2, 1.


Tôi đã không chạy nó - ????mặc dù điều này sẽ không sản xuất , thay vì ???như OP có? (Huh.) Codepad.org/nDkxGUNi không sản xuất ???.
usr2564301

7
@Jongware 10 đã chia trên lần lặp đầu tiên. Vì vậy, các giá trị được đánh giá bởi vòng lặp là 5, 2, 1 và 0. Vì vậy, nó chỉ in ra 3 lần.
MysticXG

42

Chúng ta hãy đi qua biểu thức từ trái sang phải:

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

Điều đầu tiên tôi nhận thấy là chúng tôi đang sử dụng toán tử ternary từ việc sử dụng ?. Vì vậy, sự phụ thuộc:

0xFULL ? '\0' : -1

đang nói "nếu 0xFULLlà khác không, trả về '\0', ngược lại -1. 0xFULLlà một chữ thập lục phân với hậu tố dài không dấu - có nghĩa là nó là một kiểu chữ thập lục phân unsigned long long. Điều đó thực sự không quan trọng, bởi vì 0xFcó thể nằm trong một số nguyên thông thường.

Ngoài ra, toán tử ternary chuyển đổi các loại của các điều khoản thứ hai và thứ ba thành loại phổ biến của chúng. '\0'sau đó được chuyển đổi thành int, chỉ là 0.

Giá trị của 0xFcách lớn hơn 0, vì vậy nó vượt qua. Biểu thức bây giờ trở thành:

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

Tiếp theo, :>là một máy vẽ . Nó là một cấu trúc mở rộng tới ]:

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

>>=là toán tử dịch chuyển bên phải đã ký, chúng ta có thể tạo khoảng trống từ đó ađể làm cho nó rõ ràng hơn.

Hơn nữa, <:là một sơ đồ mở rộng tới [:

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

0X.1P1là một chữ thập lục phân với số mũ. Nhưng bất kể giá trị, !!bất cứ điều gì khác không là đúng. 0X.1P10.125khác không, vì vậy nó trở thành:

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

Đây >>=là toán tử dịch chuyển bên phải đã ký. Nó thay đổi giá trị của toán hạng bên trái của nó bằng cách dịch chuyển các bit của nó về phía trước theo giá trị ở phía bên phải của toán tử. 10trong nhị phân là 1010. Vì vậy, đây là các bước:

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

>>=trả về kết quả hoạt động của nó, miễn là sự dịch chuyển a[0]vẫn khác không cho mỗi lần bit của nó được dịch chuyển sang phải một, vòng lặp sẽ tiếp tục. Nỗ lực thứ tư là nơi a[0]trở thành 0, vì vậy vòng lặp không bao giờ được nhập.

Kết quả là, ?được in ba lần.


3
:>là một máy vẽ , không phải là một bức tranh. Nó không được xử lý bởi bộ tiền xử lý, nó chỉ đơn giản được công nhận là mã thông báo tương đương với] .
Keith Thompson

@KeithThndry Cảm ơn
0x499602D2

1
Toán tử ternary ( ?:) có một loại là loại phổ biến của các điều khoản thứ hai và thứ ba. Thuật ngữ đầu tiên luôn luôn là một điều kiện và có một loại bool. Vì cả hai điều khoản thứ hai và thứ ba có loạiint , kết quả của hoạt động ternary sẽ int, không unsigned long long.
Corey

2
@KeithThndry nó có thể được xử lý bởi bộ tiền xử lý. Bộ tiền xử lý phải biết về sơ đồ vì ###có các mẫu sơ đồ; không có gì ngăn cản việc thực hiện dịch các bản dịch sang các bản không phân tích trong các giai đoạn dịch sớm
MM

@MattMcNabb Đã lâu lắm rồi tôi mới phải biết điều này, nhưng IIRC do hậu quả của các yêu cầu khác, các máy in phải ở dạng sơ đồ cho đến khi pp-tokens được chuyển đổi thành token 7).
zwol
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.