Cus*_*alc 291 c obfuscation bit-shift literals digraphs
由同事作为一个谜题,我无法弄清楚这个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;
}
Run Code Online (Sandbox Code Playgroud)
Ilm*_*nen 465
这条线:
while( a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ] )
Run Code Online (Sandbox Code Playgroud)
包含有向图 :>和<:,分别转换为]和[,因此它相当于:
while( a[ 0xFULL?'\0':-1 ] >>= a[ !!0X.1P1 ] )
Run Code Online (Sandbox Code Playgroud)
字面值0xFULL与0xF(为十六进制15)相同; 在ULL刚刚规定,这是一个unsigned long long文字.在任何情况下,作为一个布尔值,它是真的,所以0xFULL ? '\0' : -1求值为'\0',这是一个字符文字,其数值很简单0.
同时,0X.1P1是一个等于2/16 = 0.125 的十六进制浮点字面值.在任何情况下,非零,它也是一个布尔值,所以否定它!!再次产生1.因此,整个过程简化为:
while( a[0] >>= a[1] )
Run Code Online (Sandbox Code Playgroud)
运算符>>=是一个复合赋值,它将左操作数右移位右操作数给出的位数,并返回结果.在这种情况下,右操作数a[1]始终具有值1,因此它等效于:
while( a[0] >>= 1 )
Run Code Online (Sandbox Code Playgroud)
或者,等效地:
while( a[0] /= 2 )
Run Code Online (Sandbox Code Playgroud)
初始值为a[0]10.在向右移动一次后,它变为5,然后(向下舍入)2,然后是1,最后是0,此时循环结束.因此,循环体被执行三次.
jua*_*nza 67
它是涉及一些相当模糊代码有向图,分别<:与:>这对替代令牌[和]分别.还有一些使用条件运算符.还有一个位移位运算符,右移位分配>>=.
这是一个更易读的版本:
while( a[ 0xFULL ? '\0' : -1 ] >>= a[ !!0X.1P1 ] )
Run Code Online (Sandbox Code Playgroud)
和一个更易读的版本,替换[]他们解决的值中的表达式:
while( a[0] >>= a[1] )
Run Code Online (Sandbox Code Playgroud)
更换a[0]和a[1]他们的价值观应该可以很容易找出循环是干什么的,即相当于:
int i = 10;
while( i >>= 1)
Run Code Online (Sandbox Code Playgroud)
这是在每次迭代中简单地执行(整数)除以2,产生序列5, 2, 1.
0x4*_*2D2 41
让我们从左到右的表达式:
a[ 0xFULL?'\0':-1:>>>=a<:!!0X.1P1 ]
Run Code Online (Sandbox Code Playgroud)
我注意到的第一件事是我们正在使用三元运算符?.所以子表达式:
0xFULL ? '\0' : -1
Run Code Online (Sandbox Code Playgroud)
是说"如果0xFULL是非零,则返回'\0',否则-1.0xFULL是带有无符号long-long后缀的十六进制文字 - 意味着它是类型的十六进制文字unsigned long long.但这并不重要,因为它0xF可以适合常规整数.
此外,三元运算符将第二项和第三项的类型转换为它们的公共类型.'\0'然后转换为int,只是0.
值的0xF大于零,所以它通过了.表达式现在变为:
a[ 0 :>>>=a<:!!0X.1P1 ]
Run Code Online (Sandbox Code Playgroud)
接下来,:>是一个有向图.它是一个扩展为]:
a[0 ]>>=a<:!!0X.1P1 ]
Run Code Online (Sandbox Code Playgroud)
>>=是签名的右移操作符,我们可以将其从中a移除以使其更清晰.
此外,<:是一个有向图的扩展到[:
a[0] >>= a[!!0X.1P1 ]
Run Code Online (Sandbox Code Playgroud)
0X.1P1是带指数的十六进制文字.但无论价值如何,!!任何非零的东西都是正确的.0X.1P1是0.125非零,所以它变成:
a[0] >>= a[true]
-> a[0] >>= a[1]
Run Code Online (Sandbox Code Playgroud)
这>>=是签署的右移运营商.它通过将其位向前移动操作员右侧的值来改变其左操作数的值.10二进制是1010.所以这里是步骤:
01010 >> 1 == 00101
00101 >> 1 == 00010
00010 >> 1 == 00001
00001 >> 1 == 00000
Run Code Online (Sandbox Code Playgroud)
>>=返回其操作的结果,因此只要a[0]每次将其位右移1时移位保持非零,循环就会继续.第四个尝试是哪里a[0]变0,所以从未进入循环.
结果,?打印三次.