Have fun with unix

突然想起小伙伴前段时间给我看的一段神仙代码。实在特有诱惑力了。。当时女票喊我吃饭我都在看这段代码,话不多说,放码过来。

1
printf(&unix["\021%six\012\0"],(unix)["have"]+"fun"-0x60);

要解开这道题,我们首先要知道下面几点。

  • a[3] == 3[a]
    转换为指针角度看, a[3] = *(a+3) = *(3+a) = 3[a]
  • "abcdefg"[3] 这种表示相当于 char str[]="abcdefg"; str[3]
  • 知道了上面那点后,我的目光就聚焦在 unix 上,我感觉它应该是一个数字,一开始以为它是个变量,但代码中没有描述,我问小伙伴这是不是全量代码,小伙伴说文章里没有看到其他的。。于是我就开始猜他的值,一开始猜的 3,是因为忘记了字符串中 “\0xx” 的意义,把他们当成了 3 个字符来看。其实它们是一个字符的,这种格式表示八进制的 ASCII 码。然后猜 unix 值为 1,后面才晓得 unix 是个宏定义, #define unix 1,这样,我们可以将上面的题目转换成较易看懂的。
1
printf( &("\021%six\012\0"[1]), "have"[1]+"fun"-0x60);

众所周知, printf() 的第一个参数为字符串,或者说字符串的起始地址,我们分析后可知为 %six\012\0, '%s' 表示一个字符串,故我们看第 2 个参数, have[1] 也便是 'a''a'-0x60 = 97 - 96 = 1, "fun"+1 也便是字符串 "un" 的起始地址,\012 = 10, 对应 ASCII 码也便是 \n

所以这段代码其实是 printf("unix\n");

unix

很棒很惊艳的代码。

再延伸一下吧,平常打 ACM 的时候我经常会用到 printf("%d%c", ans[i], " \n"[i==n-1]) 表示每个之间用空格隔开,最后一个后面换行。