• 国际C语言混乱代码大赛优胜作品详解之“A clock in one line”


    原文链接:https://blog.csdn.net/herorenme/article/details/8864351

    摘要:IOCCC,即国际混乱C语言代码大赛是一项著名的国际编程赛事迄今已举办22届,比赛的目的在于写出最富创意、最让人难以理解的C语言代码。本文解读了19届IOCCC优胜作品“A clock in one line”的工作原理,望对您有益。

    下面这段代码即为第19届 IOCCC(国际混乱C语言代码大赛)优胜作品:“A clock in one line”。

    1
    main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

    输出结果如下:(当前时间)

    1
    2
    3
    4
    5
    6
    7
    !!  !!!!!!              !!  !!!!!!              !!  !!!!!!
    !!  !!  !!              !!      !!              !!  !!  !!
    !!  !!  !!              !!      !!              !!  !!  !!
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!!
    !!      !!              !!      !!              !!  !!  !!
    !!      !!              !!      !!              !!  !!  !!
    !!  !!!!!!              !!      !!              !!  !!!!!!

    它究竟是如何做到的呢?下面为你解读:

    首先,将这段代码格式化:

    1
    2
    3
    4
    5
    6
    main(_) {
        _^448 && main(-~_);
        putchar(--_%64
            ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
            : 10);
    }

    引入变量:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    main(int i) {
        if(i^448)
            main(-~i);
        if(--i % 64) {
            char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
            char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
            putchar(32 | (b & 1));
        else {
            putchar(10);// newline
        }
    }

    根据补码的规则,可得-~i == i+1,所以:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    main(int i) {
        if(i != 448)
            main(i+1);
        i--;
        if(i % 64 == 0) {
            putchar(' ');
        else {
            char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
            char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
            putchar(32 | (b & 1));
        }
    }

    另外,因为C语言中a[b]等同于b[a],同时在运用 -~=1+ 规则,可得:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    main(int i) {
        if(i != 448)
            main(i+1);
        i--;
        if(i % 64 == 0) {
            putchar(' ');
        else {
            char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
            char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
            putchar(32 | (b & 1));
        }
    }

    将递归转换成循环,同时再做简化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // please don't pass any command-line arguments
    main() {
        int i;
        for(i=447; i>=0; i--) {
            if(i % 64 == 0) {
                putchar(' ');
            else {
                char t = __TIME__[7 - i/8%8];
                char a = ">'txiZ^(~z?"[t - 48] + 1;
                int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
                if((i & 2) == 0)
                    shift /= 8;
                shift = shift % 8;
                char b = a >> shift;
                putchar(32 | (b & 1));
            }
        }
    }

    这样每次迭代会输出一个字符,每第64个字符会输出新的一行。

    另外,它还使用数据表来设定输出形式,决定输出的是字符32(即字符空格)还是字符33(即字符 ! )。第一个表“>'txiZ^(~z?”是一组10位图,描述每个字符的外观;第二个表 “;;;====~$::199”的作用是,从位图中选择合适的位元来展示。

    第二个表

    我们先检查一下第二个表,“int shift = ";;;====~$::199"[(i*2&8) | (i/64)];”其中 i/64 是行数(从6到0);而 i*2&8 当且仅当i为4、5、6、7mod8时为8。

    “if((i & 2) == 0) shift /= 8; shift = shift % 8”选择表的高8位(i%8=0、1、4、5)或者低8位(i=2、3、6、7)值。因此转换表最终看起来是这个样子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    row col val
    6   6-7  0
    6   4-5  0
    6   2-3  5
    6   0-1  7
    5   6-7  1
    5   4-5  7
    5   2-3  5
    5   0-1  7
    4   6-7  1
    4   4-5  7
    4   2-3  5
    4   0-1  7
    3   6-7  1
    3   4-5  6
    3   2-3  5
    3   0-1  7
    2   6-7  2
    2   4-5  7
    2   2-3  3
    2   0-1  7
    1   6-7  2
    1   4-5  7
    1   2-3  3
    1   0-1  7
    0   6-7  4
    0   4-5  4
    0   2-3  3
    0   0-1  7

    或者显示为表格的形式:

    1
    2
    3
    4
    5
    6
    7
    00005577
    11775577
    11775577
    11665577
    22773377
    22773377
    44443377

    注意:作者在表格的前两位使用了null terminator。(真狡猾!)

    第一个表

    __TIME__是预处理器定义的特殊的宏,它能扩展为一个字符串,内容为预处理器运行的时间,格式为“HH:MM:SS”,刚好占8个字符。注意:数字0-9的ASCII值为48-57,“:”的ASCII值为58。而每行输出64个字符,因此 __TIME__ 的每个字符有8个字符的空间。

    “7 - i/8%8”是当前正在输出的 __TIME__ 的索引(其中“7-”是必须的,因为我们从 i 开始向下遍历)。因此 t 即 __TIME__ 要输出的字符。

    a的值取决于t,对应关系如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    0 00111111
    1 00101000
    2 01110101
    3 01111001
    4 01101010
    5 01011011
    6 01011111
    7 00101001
    8 01111111
    9 01111011
    : 01000000

    每个数字都是一个位图,描述7段显示的字符。又因为是7位ASCII,所以高位会被清除,所以7位永远是空格,所以第二个表是这个样子:

    1
    2
    3
    4
    5
    6
    7
    000055 
    11  55 
    11  55 
    116655 
    22  33 
    22  33 
    444433

    举个例子,4即01101010(1、3、5、6位显示),输出如下:

    1
    2
    3
    4
    5
    6
    7
    ----!!--
    !!--!!--
    !!--!!--
    !!!!!!--
    ----!!--
    ----!!--
    ----!!--

    理解了吗?现在我们再对输出做一些调整:

    1
    2
    3
    4
    5
    6
    7
      00 
    11  55
    11  55
      66 
    22  33
    22  33
      44

    可以编码为“?;;?==? '::799x07”。

    出于美观考虑,我们把对64位做一些修改(因为输出仅使用低6位,所以不会受到影响),于是就变成了“?{{?}}?gg::799G”(注意:第8位并没有被使用,因此我们还可以做更多的衍生创作)。

    现在代码就变成了:

    1
    main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

    输出结果如下:

    1
    2
    3
    4
    5
    6
    7
          !!              !!                              !!  
    !!  !!              !!  !!  !!  !!              !!  !!  !!
    !!  !!              !!  !!  !!  !!              !!  !!  !!
          !!      !!              !!      !!                  
    !!  !!  !!          !!  !!      !!              !!  !!  !!
    !!  !!  !!          !!  !!      !!              !!  !!  !!
          !!              !!                              !!

    如预期的一样,看来我们的想法并没有错。

  • 相关阅读:
    Android Studio AVD和SDK Manager灰色不能点击的问题。
    回溯:最佳调度问题
    回溯:八皇后问题(蒟蒻)
    usaco1.4.3等差数列
    单调队列练习题(oj p1157 p1158 p1159)
    OJP1147括号匹配加强版(栈)与P1153乱头发节(单调栈)
    NOIP2017游记......
    火柴棒等式c++
    潜伏者(noip09年t1)解题报告 C++
    2016noipday1t1玩具迷题结题报告
  • 原文地址:https://www.cnblogs.com/hzk001/p/11693882.html
Copyright © 2020-2023  润新知