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

问题描述

这段令人着迷的代码在国际混淆C代码竞赛的第一版(1984)中得到了体现:

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));
  }
}

有一个编译器错误需要解决:

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 signal) {
      |  ^~~~

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

这样准确吗?我想念什么吗?如今,使该代码编译所需的最少更改是什么?

我在Makefile中使用了GCC 9.2.0和建议的build命令。

如果我错过了很明显的事情,请先谢谢您的歉意!

解决方法

tl; dr; ,您的错误是为ANSI C原型提供了main函数(即,将i更改为int 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’

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

cpp -traditional-cpp -P decot.c > decot1.c

在添加了正确的函数声明并添加了强制类型转换之后(请参见本答案末尾的diff&结果),您将在c89中获得一个带有单个警告的编译内容(在c99中有多个警告),并且,as described,将一些垃圾输出到stdout:

$ 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);  /*
\

在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)));}}}

diff

$ 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)));}}}
,

您不能再按原样编译原始代码。 GCC声称支持的最早的C标准是C89,该竞赛的代码来自于此。在将其移植到更现代的编译器上,您已经做得很好。但是,剩下的问题不仅仅是main()的参数数目:

  • Clang和GCC都知道sin()返回double,即使您没有#include <math.h>,也拒绝将double添加到{{1 }}在子表达式int *中。 TCC似乎接受了。
  • 已声明但未定义变量k+sin(signal),链接器将抱怨未定义的引用。

请注意,可以通过避免使用fl00r之类的组合(例如x)来由TCC编译原始代码(如今,预处理器仅替换完整的令牌,而<<x被视为一个令牌),并通过三个参数调用<<x

您的代码版本可以由GCC删除main()语句,然后再回到使用#include的方式进行编译,但可以这样声明:

floor()

为避免抱怨floor(); ,请使用sin()编译器标志。然后通过调用-fno-builtin来解决main()的问题。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...