冷知识:Hello World 的 N 种写法

接手了一个公众号,每个月有一些文章篇数的需求。摘录一二自觉质量尚好者,存档于此。

对于绝大多数人而言,跳进编程大坑的第一步,就是让电脑吐出一句「Hello World!」。如果是学过 C 语言的同学,应该对以下这段代码倒背如流:

#include <stdio.h>

int main()
{
   printf("Hello World!");
}

亲切的代码,对吧?但在后文中,你将会看到,如此熟悉的一段代码是怎样变得无比 tricky、最后面目全非的。曾有人因为如下的内容被刷新了三观,所以请先坐和放宽——准备好了吗?

进阶一:不使用引号

好,第一关,不使用引号输出「Hello World!」。任何引号,不管是「”」还是「’」。

这一关非常简单,如果你不会,重修吧。相信你肯定知道,C 语言里的字符串,只是一串 char 类型变量的数组,以 结束。那么,只需要把原文中的字符串改写为 char 数组格式,并且利用 C 的隐式类型转换,让它自动从数字(ASCII 码)转换为 char 类型。

综上,写出的代码如下:

#include <cstdio.h>

char msg[] = {72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33, 0};
int main()
{
   printf(msg);
}

如果你解决了这一关,说明你对 C 这门高级语言的「低级」之处有了基本的认识。C 里面的变量本质上都是数字,使用不同的解释方法,就有不同的表示。

进阶二:不使用分号

不使用分号,要如何执行 printf 函数呢?我们知道,一个完整的语句以分号结尾……诶,是不是有点不对?

的确,不是所有的语句都需要以分号结尾的。# 开头的指令自然如此,另外还有一个例外,那就是语句块!

例如,一行 if (1) {} 也是完整的语句,并不需要分号结尾。可是,如果加入 printf 依然需要分号……吗?其实只要放到条件里面就可以啦。

于是,最终代码也就呼之欲出了:

#include <cstdio.h>
int main()
{
   if (printf("Hello World!")) {}
}

在这里,printf 的返回值被当作了 if 的判断内容,而 if 体里面是空的。程序在进行判断的时候,会计算 printf 的返回值,自然也就将其执行了一遍。

同理,switch 结构也可以用在这一关中,具体代码就不写上来了。

进阶三:不使用「#」

不使用 #,看上去就 include 不了头文件了,那么不就没有 printf 函数了?某些会自动补全头文件、坑了无数 OI 选手的 IDE 请自觉去世(说的就是你,Dev-C++!)。

……成,现在 gcc 也会隐式包含 stdio.h 了,但是会有 warning 诶……虽说 warning 是没人理的小透明,但如果要 0 warning 编译,要怎么做呢?

这里需要用到的就是一些罕见特性了,比如……你知道 %: 会被自动转义成 # 吗?那就是大名鼎鼎(并不)的二元转义字符。

也不知道为啥,标准里规定了许多组二元转义字符,会在编译时把某些二元字符组转换为单个字符,列表如下:

二元字符组 等价字符
<: [
:> ]
<% {
%> }
%: 或 %% #

 

看了一下,之所以规定这些转义字符,主要是因为有的语言的键盘上没有右侧这些字符。

另外,还有很多组三元转义字符,但是默认不开启,需要给编译器传递参数。

所以嘛……进行一下简单的替换,就是这一关的答案:

%:include

int main()
{
   printf("Hello World!");
}

进阶四:不使用任何括号

咳,经过三道(相对)无关痛痒的关卡,到了真正硬核的一关,请注意自己的三观:如何在不使用任何括号(「({<[]>})」中的任意字符)的情况下,打印出 Hello World!

小编在第一次看到这一题的时候,也感觉不可思议、莫名其妙:不能使用括号、花括号,那岂不是函数都不能调用?连 main 函数都不能定义?那还写个毛 C 啊!就是这道题曾让人三观崩坏,可是如果不能想通这道题的第一种解法,说明你对 C、操作系统、计算机的理解还不够通透。

尽管看上去这一关是无解的,可事实是,这一关至少有两种解法。一种触及 C 底层的核心内涵,另一种则是巧妙的构造。

准备好刷新自己的三观了吗?第一种解法如下:

const int main = 1214185834;
const int main2 = 931213;
const int main3 = 208273408;
const int main4 = 267946330;
const int main5 = 1795109125;
const int main6 = 84891708;
const int main7 = 1819043144;
const int main8 = 1867980911;
const int main9 = 560229490;

没错,你没眼花,我也没打错,这段代码编译后能够输出「Hello World!」!不信请看下面的截图:

当然,你也可以实践出真知。但请注意,以上这段代码须在 64 位的 Linux 系统下编译运行,否则跑不起来的哦~

为什么这一串神秘数字能够输出「Hello World!」?为什么会有编译、运行环境的限制?正如前面所说,这些触及 C、计算机、操作系统的核心内涵(别打我)。然而篇幅所限,在这里就不详细展开了,期待未来会有机会专门写一篇详谈~

至于另一种取巧的解法,很是巧妙,充分利用了编译器可以当文本编辑器的特点。答案就不贴出来了,有兴趣的欢迎自己试验玩玩。

以上,这就是今日份的冷知识。更多有趣又有用(?)的小知识,敬请关注我们的「冷知识」频道。

我们是电子科技大学百度校园菁英俱乐部。

如果你有兴趣与我们一起学习、分享知识,或与我们一起出去见见外面的世界,欢迎加入我们!

请实时关注我们的动态,同时也可以在公众号内留言。

评论已关闭