3

我在ioccc中看到了这个。

    int i;
    main()
    {
        for(;i["]<i;++i){--i;}"];read('-'-'-',i+++"hello, world!\n",'/'/'/'));
    }
    read(j,i,p)
    {
        write(j/p+p,i---j,i/i);
    }

它打印出“你好世界!” 用 gcc 编译时,不确定它是否适用于 Visual Studio 12。有人可以向我解释一下它到底是如何工作的吗?

4

3 回答 3

4

由于从问题中删除了原始代码并重新格式化了一个被替换的代码,因此我从主题启动器给出的代码开始。

int i;main(){for(;i["]<i;++i){--i;}"];
read('-'-'-',i+++"hello, world!\n",'/'/'/'));
}read(j,i,p){write(j/p+p,i---j,i/i);}

从恢复结构开始。在它们所属的位置添加空格和换行符。请记住,Cint在未指定时假定数据类型。

int i;

/* int */ main()
{
  for( ; i["]<i;++i){--i;}"];
          read(   '-' - '-',
                   i++ + "hello, world!\n",
                   '/' / '/') );
}

/* int */ read( /* int */ j,i,p)
{
  write( j/p + p, i-- - j, i/i );
}

请注意,在 C 语言中:

  1. 没有“字符”类型。一个字母只是一个(通常是 8 位)整数。因此'-' - '-'是 0 和'/' / '/'1(就像i/i后面一样)
  2. 没有“字符串”类型——这只是一个指向“字符”的指针
  3. 根据定义,数组和指针是同一个东西
  4. 无大小(字节大小)的指针和整数相互隐式转换。

注意:在下面的评论中,人们认为在 C 数组中不是指针,而只是隐式类型转换为指针。这在理论上可能是正确的,但似乎对这段特定代码没有任何实际意义。但是,项目符号 3 可能被书本错误地表述了。

还让我们假设所有全局变量都是零初始化的。

因此i["]<i;++i){--i;}"]是 0[指向某个字符串的指针],然后是 1[指向某个字符串的指针],等等。实际上是字符串的第 i 个字符。
它用于第二个参数位置的 for 循环 - 条件检查器。所以实际上这可以重新表述为

for( ; "]<i;++i){--i;}"[i] != '\0'; read... );

或通过 C for 循环的定义

while ( "]<i;++i){--i;}"[i] != 0 ) { read ...; }

两行同样有 14 个字符,这不是巧合:
"hello, world!\n"
"]<i;++i){--i;}"

这使得它只是字符串中每个字符的循环,您可以将其重新表述为
for i := 0 to 14-1 do

把这一切粘在一起,程序变成

int i;

/* int */ main()
{
  for( i=0; i<strlen("hello, world!\n"); i++)
  {
    read( 0, & "hello, world!\n"[i], 1);
  }
}

是不是容易一点?现在转到函数本身......以及 C 文件句柄。根据 UNIX 标准,预定义了以下文本文件:

  • 0 - STDIN - 从键盘读取
  • 1 - STDOUT - 打印到显示器/打印机/日志文件/等
  • 2 - STDERR - 关于错误情况的紧急打印

因此:

/* int */ read( j = 0, i = pointer-to-letter-to-print, p = 1)
{
  write( j/p + p, // 0/1 + 1  == 1 == STDOUT 
         i-- - j, // == i itself due to j == 0; 
                  // -- is POST-decrement and does not affect the calculation
         i/i /*== 1 as long as i != 0  - meaning print only single character*/ );
}

再次将其粘合在一起:

int i;

int my_read( int j, char * pChar, int p)
{
  write( STDOUT, pChar, 1 );
  pChar--; // does not affect anything, as mentioned above
}

const char msg[] = "hello, world!\n";
int main(void)
{
  for( i=0; i<strlen(msg); i++)
  {
    my_read( 0, &msg[i], 1);
  }
}

你能弄清楚这个片段现在是如何工作的吗?;-)

只需添加空格和新行 - 正确的代码结构格式 - 它变得容易小便:-)

于 2013-06-08T12:55:52.287 回答
1
  • 结果'-'-'-'0
  • '/' / '/' 的结果是1

    所以,read('-'-'-',i+++"hello, world!\n",'/'/'/'));

    实际上是

    read(0,i+++"hello, world!\n",1));

  • 关于循环:for(;i["]<i;++i){--i;}"];

    i["]<i;++i){--i;}"]

    实际上是

    "]<i;++i){--i;}"[i]

    因为在C语言中,如果写成a[i],就相当于i[a]( *(a+i) == *(i+a))

    所以这个 for 循环将继续进行,直到遇到最后一个字符。

把它们放在一起,很容易理解这里发生了什么。(我在此链接中找到了更多解释:http ://www.cs.washington.edu/education/courses/cse142/97su/josh/obfuscate.html )

于 2013-06-08T12:55:37.237 回答
0

让我们先把它格式化一下:

int i;


main()
{
   for(;i["]<i;++i){--i;}"];
       read('-'-'-',i++ + "hello, world!\n",'/'/'/'));
}


read(j,i,p)
{
  write(j/p+p,i---j,i/i);
}

其次,我们看看它实际上做了什么:

for(;i["]<i;++i){--i;}"];

是相同的

for(;"]<i;++i){--i;}"[i];

因此,无需初始化,只要带有“C 代码”索引的字符串i不为零。

然后是 for 循环的更新部分:

read('-'-'-',i++ + "hello, world!\n",'/'/'/'));

'-'-'-'就像是零一样'a'-'a'i++ + "hello, world!"是一个指针+偏移量,当然更新'i'。最后一部分是将某物自除,即为 1。

最后一个技巧是一个重新定义的read函数,它调用 write:

read(j,i,p)
{
  write(j/p+p,i---j,i/i);
}

它使用类似的技巧来产生stdoutj/p+p 的值(p = 1 从上面,j = 0,所以 0/1+1 = 1)和要写入的数据长度 (1)。要写的字符是i-- -j,既然我们i在做之前取--,它没有效果[不是使用全局的i,而是局部的read

于 2013-06-08T13:05:50.767 回答