花式秀之Hello world

本文展示了10种别出心裁的“Hello World!”程序编写方法,从宏定义到模板特化,再到利用C/C++的底层特性,每一种都富有创意且各具特色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在初学一门编程语言的时候,写一个“Hello world!”程序是最常见的入门方法。通过写一个成功的“Hello world!”,可以实践这门语言最基本的语法特性,还可以带给自己成就感,真是一举两得。C/C++语言本身有很多特性,如果能够将这些技术分解出来变成一个个的“Hello world!”,并且将这些技术点到为止,貌似也算是一件善事。这里,列举了10个“Hello world!”程序,大家雅俗共赏一下。

1. 最经典的“Hello world!”

“Hello world!”最经典的写法当然是直接用 printf 输出“Hello world!”这几个字符了。无论用C还是 C++,写起来都非常的简洁明了。这里把最常见的几个全部列在下面。

#include <stdio.h>
#include <iostream>
int main()
{
printf(“Hello world!”);                   // 教科书的写法
puts(“Hello world!”);                     // 我最喜欢的
puts(“Hello” ” ” ”world!”);               // 拼接字符串
std::cout << ”Hello world!” << std::endl; // C++风格的教科书写法
return 0;
}
特别需要注意的是,在C/C++里,如果两个字符串之间除空白符以外没有任何东西,编译器会自动认为这两个字符串是连在一起的字符串。这样,如果一个字符串过长,可以用这种方法换行来写,既不浪费性能,又美观。

2. 用宏写的“Hello world!”

在C/C++里,宏是一个神奇的东西。特别是在C语言中,宏可以帮我们做一些“又脏又累”的活,包括拼接代码片断、隐藏繁琐的实现细节等等。其中特别有趣的是“#”的用法,它可以“提取”参数的名字,把它变成字符串。
#include <stdio.h>
#define Say(sth) puts(#sth)
int main()
{
return Say(Hello world!);
}
请注意,这个Hello world可是完全没有出现引号哦!

3. 断章取义的“Hello world!”

字符串是一种常量这当然毫无疑问,但是它的类型是什么,这就需要考虑一下了。使用C++的typeid就可以这个问题的答案,而且只要是符合C或C++标准的编译器就应该是一样的结果。比如字符串“Hello world!”,它的类型就是 char const [13]。
知道了这个,就可以写出以下的“Hello world!”:
#include <stdio.h>
int main()
{
return puts(&”Do not say: Hello world!”[12]);
}

4. 退出时运行的“Hello world!”

大家都知道 main 函数退出意味着程序结束,可是这并不完全正确,我们完全可以在 main 函数退出以后做很多事呢–比如说,输出“Hello world!”。这个功能依赖于C标准库中提供的函数 atexit(),调用这个函数并注册自己的回调函数就行。需要注意,这个函数可以调用多次,最后注册的函数最先执行。
#include <stdio.h>
#include <stdlib.h>
void say()
{
printf(“world!”);
}
void sth()
{
printf(“Hello ”);
}
int main()
{
return atexit(say), atexit(sth);
}

5. 读取自己的“Hello world!”

C/C++的编译器提供了一些有用的内置宏,最常用的就是 __FILE__ 和 __LINE__ 了。其中,__FILE__ 代表当前的源文件的文件名,嗯,对了,如果我们让这个程序读取自己的源文件,不就可以做一个很有意思的“Hello world!”了么?
// Hello world!
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::ifstream ifs(__FILE__);
std::string say, some, word;
ifs >> say >> some >> word;
std::cout << some << ” ” << word;
return 0;
}

6. 话分两头的“Hello world!”

有了C++的类,我们就可以光明正大的在 main 函数执行之前和之后做感兴趣的事情了。我们可以声明一个全局的类的实例,这样,在 main 函数执行之前会调用这个类的构造函数,结束之后则会调用析构函数。
#include <iostream>
// 本文转自 C++Builder研究 – http://www.ccrun.com/article.asp?i=999&d=v433i2
class say
{
public:
say()
{
std::cout << ”Hell”;
}
~say()
{
std::cout << ”world!”;
}
}hello;
int main()
{
std::cout << ”o ”;
return 0;
}

7. 传入模板的“Hello world!”

C++的模板功能极为强大,可以说是C++里面最艰深、最经典、最时尚的部分。一个“Hello world!”当然无法使用很多很高级的模板技巧,我也不想只使用模板特化这样无阻挂齿的小技巧,嗯,那就来演示一个比较罕见的用法吧。
#include <iostream>
template <char * words>
class say
{
public:
void operator () ()
{
std::cout << words;
}
};
extern char hello[] = ”Hello world!”;
int main()
{
return say<hello>()(), 0;
}
请注意,这个 extern 是十分必要的,只有加上了 extern,这个指针才是一个编译器间可以确定的值,也才可以参与模板运算。还有,hello 必须为数组类型,而不能为 char*,这个道理和加 extern 是一样的。
此外,这里还演示了 functor 的用法,嗯,关于它的优点就不在这里多说了,反正是与原生指针相比有很多好处就是了。

8. 调用私有函数的“Hello world!”

我们知道,C++类的私有函数是不能被外界访问的,比如说 main 函数里面,它绝对不能访问类的私有函数,除非把它设为类的友元函数。不过我们还是可以用一些比较奇怪的方法访问类的私有函数–当然,这个私有函数必须满足一个条件:它是虚函数。
这里就涉及到一个问题,指向虚函数的虚表放在哪里?对于 VS.Net 2003 而言,虚表是类的第一个成员,虚函数指针按照函数声明的顺序放在虚表里面。当然,这个说法并不严谨,更细节的东西还是去看看那本“成人高钙奶粉”吧,它会给出最权威的解答。
这里是一个很有意思的例子:
#include <iostream>
#include <cstddef>
class secret
{
private:
virtual void say()
{
std::cout << ”Hello world!”;
}
};
int main()
{
secret word;
(reinterpret_cast<void (*)()>(**(intptr_t**)(&word)))();
return 0;
}

9. 最暴力的“Hello world!”

最暴力的调用函数的方法是:直接修改函数的返回地址,让这个地址指向我们想要调用的函数。这也就是缓冲区溢出漏洞的应用方法了,不过里面还涉及到很多问题,在这里就不一一列举,想要了解的话,还是去 Google 吧。这里只演示一个可以在 VS.Net 2003 下可以用的“Hello world!”。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
void say()
{
puts(“Hello world!”);
exit(0);
}
int main()
{
volatile intptr_t a = 0;
volatile intptr_t * p = &a;
*(p + 2) = (intptr_t)say;
*(p + 3) = (intptr_t)say;
return 0;
}

10. 外星人说的“Hello world!”

好了,这个“Hello world!”是最匪夷所思的一个了!不过它并没有涉及任何复杂的C/C++语言特性,只是看起来有点酷。你能看懂外星人在说什么不?
#include <stdio.h>
void alien_say(char * p)
{
while (putchar(*(p += *(p + 1) – *p)));
}
int main()
{
return alien_say(“BETHO! Altec oh liryom(a loadjudas!) dowd.”), 0;

}

//================================================= 更牛逼的一些写法 ====================================================//

下面的六个程序片段主要完成这些事情:

  1. 输出Hello, World
  2. 混乱C语言的源代码

下面的所有程序都可以在GCC下编译通过,只有最后一个需要动用C++的编译器g++才能编程通过。

hello1.c

 

[cpp]  view plain copy
  1. #define _________ }  
  2. #define ________ putchar  
  3. #define _______ main  
  4. #define _(a) ________(a);  
  5. #define ______ _______(){  
  6. #define __ ______ _(0x48)_(0x65)_(0x6C)_(0x6C)  
  7. #define ___ _(0x6F)_(0x2C)_(0x20)_(0x77)_(0x6F)  
  8. #define ____ _(0x72)_(0x6C)_(0x64)_(0x21)  
  9. #define _____ __ ___ ____ _________  
  10. #include<stdio.h>  
  11. _____  

hello2.c

[cpp]  view plain copy
  1. #include<stdio.h>  
  2.   main(){  
  3.     int x=0,y[14],*z=&y;*(z++)=0x48;*(z++)=y[x++]+0x1D;  
  4.     *(z++)=y[x++]+0x07;*(z++)=y[x++]+0x00;*(z++)=y[x++]+0x03;  
  5.     *(z++)=y[x++]-0x43;*(z++)=y[x++]-0x0C;*(z++)=y[x++]+0x57;  
  6.     *(z++)=y[x++]-0x08;*(z++)=y[x++]+0x03;*(z++)=y[x++]-0x06;  
  7.     *(z++)=y[x++]-0x08;*(z++)=y[x++]-0x43;*(z++)=y[x]-0x21;  
  8.     x=*(--z);while(y[x]!=NULL)putchar(y[x++]);  
  9.   }  

hello3.c

[cpp]  view plain copy
  1. #include<stdio.h>  
  2.    #define __(a) goto a;  
  3.    #define ___(a) putchar(a);  
  4.    #define _(a,b) ___(a) __(b);  
  5.    main()  
  6.    { _:__(t)a:_('r',g)b:_('$',p)  
  7.      c:_('l',f)d:_(' ',s)e:_('a',s)  
  8.      f:_('o',q)g:_('l',h)h:_('d',n)  
  9.      i:_('e',w)j:_('e',x)k:_('/n',z)  
  10.      l:_('H',l)m:_('X',i)n:_('!',k)  
  11.      o:_('z',q)p:_('q',b)q:_(',',d)  
  12.      r:_('i',l)s:_('w',v)t:_('H',j)  
  13.      u:_('a',a)v:_('o',a)w:_(')',k)  
  14.      x:_('l',c)y:_('/t',g)z:___(0x0)}  

hello4.c

[cpp]  view plain copy
  1. int n[]={0x48,  
  2. 0x65,0x6C,0x6C,  
  3. 0x6F,0x2C,0x20,  
  4. 0x77,0x6F,0x72,  
  5. 0x6C,0x64,0x21,  
  6. 0x0A,0x00},*m=n;  
  7. main(n){putchar  
  8. (*m)!='/0'?main  
  9. (m++):exit(n++);}  

hello5.c

[cpp]  view plain copy
  1. main(){int i,n[]={(((1<<1)<<(1<<1)<<(1<<  
  2. 1)<<(1<<(1>>1)))+((1<<1)<<(1<<1))), (((1  
  3. <<1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<<(  
  4. 1<<1)<<(1<<1))+((1<<1)<<(1<<(1>>1)))+ (1  
  5. <<(1>>1))),(((1<<1)<<(1<<1)<<(1<<1)<< (1  
  6. <<1))-((1<<1)<<(1<<1)<<(1<<(1>>1)))- ((1  
  7. <<1)<<(1<<(1>>1)))),(((1<<1)<<(1<<1)<<(1  
  8. <<1)<<(1<<1))-((1<<1)<<(1<<1)<<(1<<(1>>1  
  9. )))-((1<<1)<<(1<<(1>>1)))),(((1<<1)<< (1  
  10. <<1)<<(1<<1)<<(1<<1))-((1<<1)<<(1<<1)<<(  
  11. 1<<(1>>1)))-(1<<(1>>1))),(((1<<1)<<(1<<1  
  12. )<<(1<<1))+((1<<1)<<(1<<1)<<(1<<(1>>1)))  
  13. -((1<<1)<<(1<<(1>>1)))),((1<<1)<< (1<<1)  
  14. <<(1<<1)),(((1<<1)<<(1<<1)<<(1<<1)<<(1<<  
  15. 1))-((1<<1)<<(1<<1))-(1<<(1>>1))),(((1<<  
  16. 1)<<(1<<1)<<(1<<1)<<(1<<1))-((1<<1)<< (1  
  17. <<1)<<(1<<(1>>1)))-(1<<(1>>1))), (((1<<1  
  18. )<<(1<<1)<<(1<<1)<<(1<<1))- ((1<<1)<< (1  
  19. <<1)<<(1<<(1>>1)))+(1<<1)), (((1<<1)<< (  
  20. 1<<1)<<(1<<1)<< (1<<1))-((1<<1)<< (1<<1)  
  21. <<(1<<(1>>1)))-((1<<1) <<(1<< (1>>1)))),  
  22. (((1<<1)<< (1<<1)<<(1<<1)<< (1<<1))- ((1  
  23. <<1)<<(1<<1)<<(1<<1))+((1<<1)<< (1<<(1>>  
  24. 1)))), (((1<<1)<<(1<<1) <<(1<<1))+(1<<(1  
  25. >>1))),(((1<<1)<<(1<<1))+((1<<1)<< (1<<(  
  26. 1>>1))) + (1<< (1>>1)))}; for(i=(1>>1);i  
  27. <(((1<<1) <<(1<<1))+((1 <<1)<< (1<<(1>>1  
  28. ))) + (1<<1)); i++) printf("%c",n[i]); }  

hello6.c

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #define _(_) putchar(_);  
  3. int main(void){int i = 0;_(  
  4. ++++++++++++++++++++++++++++  
  5. ++++++++++++++++++++++++++++  
  6. ++++++++++++++++++++++++++++  
  7. ++++++++++++++++++++++++++++  
  8. ++++++++++++++++++++++++++++  
  9. ++++i)_(++++++++++++++++++++  
  10. ++++++++++++++++++++++++++++  
  11. ++++++++++i)_(++++++++++++++  
  12. i)_(--++i)_(++++++i)_(------  
  13. ----------------------------  
  14. ----------------------------  
  15. ----------------------------  
  16. ----------------------------  
  17. ----------------i)_(--------  
  18. ----------------i)_(++++++++  
  19. ++++++++++++++++++++++++++++  
  20. ++++++++++++++++++++++++++++  
  21. ++++++++++++++++++++++++++++  
  22. ++++++++++++++++++++++++++++  
  23. ++++++++++++++++++++++++++++  
  24. ++++++++++++++++++++++++++i)  
  25. _(----------------i)_(++++++  
  26. i)_(------------i)_(--------  
  27. --------i)_(----------------  
  28. ----------------------------  
  29. ----------------------------  
  30. ----------------------------  
  31. ----------------------------  
  32. ------i)_(------------------  
  33. ----------------------------  
  34. i)return i;}  

 

展示了一些C语言的变态玩法。也向大家展示了一下程序是可以写得让人看不懂的,在那篇文章中,可以看到很多人的留言,很多人都觉得很好玩,是的,那本来是用来供朋友们“消遣作乐”,供娱乐娱东而已,不必太过认真。

不过,通过这种极端的写法,大家可以看到源代码都可以写得那么复杂难懂的。大家也许在赞叹之余一笑了之,而我则希望,大家能够在娱乐以后认真思考一下,你不要以为咱们自己不会把代码搞得那么复杂,只不过没有像那6个Hello World一样那么极端,不过,说句老实话,咱们每个程序都有把清晰的程序搞得一团混乱的潜能,只不过程度不一样罢了,我并不是在这里危言耸听,大家好自为之 。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值