IOCCC 1984/decot.c - 可以在 21 世纪编译吗?

Che*_*uid 8 c obfuscation deobfuscation

这段引人入胜的代码出现在第一届(1984 年)国际混淆 C 代码竞赛中:

http://www.ioccc.org/years.html#1984 (decot)

在清除了由 goto 和一些偷偷摸摸的注释引起的预处理器滥用和未使用代码的碎片后,您最终得到以下幸存代码(如果我错了,请纠正我!):

#include <stdio.h> //used to suppress warnings
#include <math.h> //used to suppress warnings
extern int fl00r; //renamed to not clash with floor from math.h - unless it's part of the trickery???
int b, k['a'] = {
    sizeof(int(*)()),
    };
struct tag {int x0,*xO;}

*main(int i, int dup, int signal) { //int added to suppress warnings
  for(signal=0;*k *= * __FILE__ *i;) {
   printf(&*"'\",=);    /*\n\\", (*((int(*)())&fl00r))(i)); //see line 3
   if(b&&k+sin(signal)/ * ((main) (b)-> xO));
  }
}
Run Code Online (Sandbox Code Playgroud)

有一个编译器错误需要克服:

decot.c: In function 'main':
decot.c:12:28: error: too few arguments to function 'main'
   12 |    if(b&&k+sin(signal)/ * ((main) (b)-> xO));
      |                            ^
decot.c:9:2: note: declared here
    9 | *main(int i, int dup, int signal) {
      |  ^~~~
Run Code Online (Sandbox Code Playgroud)

我怀疑当时编译器的工作方式意味着您可以以某种方式只用 1 个参数调用 main,即使它在这种情况下是用 3 专门定义的。

这是准确的吗?我错过了什么吗?如今,使此代码能够编译所需的最少更改是什么?

我使用 GCC 9.2.0 和 Makefile 中建议的构建命令。

如果我错过了一些非常明显的内容,请提前致谢并道歉!

G. *_*pen 5

您无法再按原样编译原始代码。GCC声称支持的最早的C标准是C89,本次比赛的代码是在此之前的。您已经在将其移植到更现代的编译器方面做得很好。但剩下的问题不仅仅是 的参数数量main()

  • Clang 和 GCC 都知道sin()返回 a double(即使您不知道#include <math.h>),并且拒绝将 a 添加double到子int *表达式中的 an 中k+sin(signal)不过, TCC似乎接受了这一点。
  • 该变量fl00r已声明但未定义,链接器将抱怨对它的未定义引用。

请注意,原始代码可以通过 TCC 进行编译,只需避免使用诸如(预处理器现在仅替换完整标记,并算作一个标记)x之类的组合,并使用三个参数进行调用即可。<<x<<xmain()

您的代码版本可以通过删除#include语句、返回到 using来由 GCC 编译floor(),但向前声明如下:

floor();
Run Code Online (Sandbox Code Playgroud)

为了避免抱怨sin(),请使用-fno-builtin编译器标志。main()然后通过像这样调用它来解决问题(main) (b,b,b)


use*_*777 5

tl;博士; 你的错误是给main函数一个 ANSI C 原型(即更改iint i等),它指示编译器检查它被调用的参数并导致该too few arguments错误。

例子:

echo 'int foo(a,b){return a+b;} int main(){return foo(3);}' |
  cc -Wall -std=c89 -xc -
# K&R C function OK, no errors

echo 'int foo(int a, int b){return a+b;} int main(){return foo(3);}' |
  cc -Wall -std=c89 -xc -
...
<stdin>:1:54: error: too few arguments to function ‘foo’
Run Code Online (Sandbox Code Playgroud)

该代码应该使用传统的C 预处理器进行预处理,而不是使用“ANSI C”预处理器。使用标准预处理器会导致一些工件,例如<< =代替<<=* =代替*=等。

cpp -traditional-cpp -P decot.c > decot1.c
Run Code Online (Sandbox Code Playgroud)

添加正确的函数声明并添加强制转换后——请参阅本答案末尾的差异和结果——您会得到一些在 c89 中编译时带有单个警告的内容(以及在 c99 中的几个警告),并且,如所述, 将一些垃圾打印到标准输出:

$ cc -std=c89 decot1.c -lm -o decot1
decot1.c: In function ‘main’:
decot1.c:13:33: warning: function called through a non-compatible type
    (printf(&*"'\",x); /*\n\\", (*((int(*)())&floor))(i)));
                                ~^~~~~~~~~~~~~~~~~~~~
$ ./decot1
'",x);  /*
\
Run Code Online (Sandbox Code Playgroud)

这与我在 V7 Unix 上编译和运行原始版本时得到的完全相同,所以它应该是正确的 ;-)

decot1.c

double floor(double);
double sin(double);
int printf(const char*, ...);
int b,
k['a'] = {sizeof(
    int(*)())
,};
struct tag{int x0,*xO;}

*main(i, dup, signal) {
{
  for(signal=0;*k *= * "decot.c" *i;) do {
   (printf(&*"'\",x);   /*\n\\", (*((int(*)())&floor))(i)));
        goto _0;

_O: while (!(k['a'] <<= - dup)) {
        static struct tag u ={4};
  }
}


while(b = 3, i); {
k['a'] = b,i;
  _0:if(b&&k+
  (int)(sin(signal)             / *    ((main) (b)-> xO)));}}}
Run Code Online (Sandbox Code Playgroud)

差异

$ diff -u decot1.c~ decot1.c
--- decot1.c~
+++ decot1.c
@@ -1,4 +1,6 @@
-extern int floor;
+double floor(double);
+double sin(double);
+int printf(const char*, ...);
 int b,
 k['a'] = {sizeof(
     int(*)())
@@ -20,4 +22,4 @@
 while(b = 3, i); {
 k['a'] = b,i;
   _0:if(b&&k+
-  sin(signal)          / *    ((main) (b)-> xO));}}}
+  (int)(sin(signal)            / *    ((main) (b)-> xO)));}}}
Run Code Online (Sandbox Code Playgroud)