IOCCC 早期的另一颗珍珠是 Larry Wall 1986 年的条目:
http://www.ioccc.org/years.html#1986 (墙)
我怀疑现在没有 C 编译器可以真正直接编译该源代码,因为它包含严重的预处理器滥用:
- 最新
TDM-GCC 9.2.0
设置为 ANSI 模式失败 - 上次
TCC 0.9.27
失败
然而,在从混淆的原始代码中提取出预处理后的代码(始终使用 GCC 的 cpp -traditional
)后,TCC 和 GCC 都设法对其进行编译;尽管如此,GCC 的努力都白费了,因为当程序试图开始解码其混淆的介绍文本时,程序会卡住(对于那些想要深入研究的人来说,这里不会破坏它!)
另一方面,TCC 成功地对 system()
、read()
和 write()
的隐式声明发出警告。并快速生成工作程序。
我尝试使用 GDB 单步执行 GCC 代码,这就是我发现编译的 GCC 代码在 for
循环的第二遍中阻塞的原因,该循环遍历文本字符串以进行解码它:
[Inferior 1(进程 9460)退出,代码为 030000000005]
该进程 ID 并不重要,因为它代表崩溃的调试构建可执行文件。 但是,退出代码保持不变。
显然,TCC 比 GCC 更适合 IOCCC 条目。后者仍然能够成功编译甚至运行一些条目,但对于像这样的棘手情况,TCC 很难被击败。它唯一的缺点是,在预处理极其滥用的代码(例如本示例)时,它会表现不佳。它在某些预处理条目之间留下了空格,因此无法将它们连接到作者想要的 C 关键字中,而 GCC 的 cpp
可以 100% 工作。
我的问题是,听起来很哲学,甚至是修辞:
与 TCC 不同,现代 GCC 中是什么导致它无法编译,或者在编译早期的 C 程序时生成不可用的代码?
提前感谢所有反馈,非常感谢!
注意:我使用的是带有 WSL 2 的 Windows 10 版本 2004; GCC 在 Windows 和 WSL 2 环境中都会失败。我也计划在 WSL 2 中编译 TCC,以便在该环境中进行比较。
PS:当这个程序最终按预期执行时,我非常喜欢它。毫无疑问,当之无愧的是当年的“困惑中最全面的大奖”!
最佳答案
What is it in modern GCC that makes it either fail to compile, or produce unusable code when it does compile, earlier C programs, unlike TCC?
未定义的行为。这更像是一条规则。看看this classic 1984 entry .
现在的 C 编译器按照 ISO 9899 标准中的规定来编译 C,该标准的第一个修订版于 1990 年(或 1989 年)发布。该计划早于该计划。值得注意的是,它使用了一些非常奇怪的传统预处理器语法,这些语法在 C89、C99、C11 等中无效。
一般的想法是,默认情况下您不希望允许此语法,因为传统预处理器不会生成与现代预处理器兼容的代码 - 例如,传统预处理器也会替换字符串内的宏:
#define greeting(thing) puts("Hello thing")
main() {
greeting(world!!!);
}
预处理到
main() {
puts("Hello world!!!");
}
该程序是有效的C89,尽管风格不好;但它会预处理为
main() {
puts("Hello thing");
}
因此,最好在出现任何非标准预处理器使用迹象时就出错,否则代码可能会被巧妙地破坏,因为不会进行此类替换。
另一件事是可写字符串。反混淆代码直接尝试修改字符串文字。 C89 指定这具有未定义的行为 - 这些会导致崩溃,因为它们映射到 GCC 编译的程序中的只读页面中。较旧的 GCC 版本支持 -fwriteable-strings
但它很久以前就被弃用了,因为无论如何它都有 bug。
我通过 GCC 9.3.0 的这些最小更改使程序运行起来。 -traditional
不再支持编译,因此您必须先进行预处理,然后再进行编译:
gcc -traditional -E wall.c > wall_preprocessed.c
perl -pi -e '/^[^#]/ && s/(".*?")/(char[]){$1}/g' wall_preprocessed.c
# thanks Larry ;)
gcc wall_preprocessed.c
即我将所有看起来像字符串文字 "..."
且不在编译器行指令(以 #
开头的行)内的内容包装到 (char []){"..."}
数组复合文字 - 众所周知,复合文字具有作用域存储持续时间,非常量限定字面量是可写的。
关于c - IOCCC 1986/wall.c - 为什么 TCC 在处理早期 C 代码方面击败了 GCC?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64199950/