一位同事给出的一个谜题,我无法弄清楚这个 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;
}
一位同事给出的一个谜题,我无法弄清楚这个 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;
}
该行:
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
包含有向图 :>
和<:
,它们分别转换为]
和[
,所以它相当于:
while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )
字面0xFULL
量与0xF
(对于 16 进制表示15
)相同;ULL
just 指定它是一个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,此时循环结束。因此,循环体被执行了 3 次。
这是一些涉及digraphs的相当晦涩的代码,即<:
和分别是和:>
的替代标记。条件运算符也有一些用途。还有一个移位运算符,右移赋值。[
]
>>=
这是一个更易读的版本:
while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )
以及一个更易读的版本,替换[]
它们解析为的值中的表达式:
while( a[0] >>= a[1] )
替换a[0]
anda[1]
为它们的值应该可以很容易地弄清楚循环在做什么,即相当于:
int i = 10;
while( i >>= 1)
这只是在每次迭代中执行(整数)除以 2,产生序列5, 2, 1
。
让我们从左到右浏览表达式:
a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]
我注意到的第一件事是我们使用的是三元运算符?
。所以子表达式:
0xFULL ? '\0' : -1
说“如果0xFULL
非零,则返回'\0'
,否则-1
.0xFULL
是带有无符号 long-long 后缀的十六进制文字 - 意味着它是类型的十六进制文字unsigned long long
。但这并不重要,因为0xF
可以放入常规整数中。
此外,三元运算符将第二项和第三项的类型转换为它们的公共类型。'\0'
然后转换为int
,也就是0
。
的值0xF
远大于零,所以它通过了。表达式现在变为:
a[ 0 :>>>=a<:!!0X.1P1 ]
接下来,:>
是一个有向图。它是一个扩展为]
:
a[0 ]>>=a<:!!0X.1P1 ]
>>=
是带符号的右移运算符,我们可以a
将其隔开以使其更清晰。
此外,<:
是一个扩展为 的有向图[
:
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]
每次其位右移一位时移位保持非零,循环就会继续。第四次尝试是 where a[0]
become 0
,因此永远不会进入循环。
结果,?
被打印了三遍。