【转】c/c++“Hello world!”的N种写法
在初学一门编程语言的时候,写一个“Hello world!”程序是最常见的入门方法。通过写一个成功的“Hello world!”,可以实践这门语言最基本的语法特性,还可以带给自己成就感,真是一举两得。C/C++语言本身有很多特性,如果能够将这些技术分解出来变成一个个的“Hello world!”,并且将这些技术点到为止,貌似也算是一件善事。这里,列举了10个“Hello world!”程序,大家雅俗共赏一下。
1. 最经典的“Hello world!”
“Hello world!”最经典的写法当然是直接用 printf 输出“Hello world!”这几个字符了。无论用C还是 C++,写起来都非常的简洁明了。这里把最常见的几个全部列在下面。
#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>
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;
}
第一次srm总结 (srm478)
白天弄了好久,下jdk,装插件,调试,熟悉代码风格,总算在19:00之前结束,今天比赛开始很早的说。
开始比赛后,先开250,水题,但由于对环境不熟悉(至今仍不知道如何调试程序),所以第一题写了很久,一遍参考别人的样例一边写的,浪费的好多时间,快40分钟才交,不过,好歹最后过了,130分,哈哈。
500其实也不难,由于对stl不熟悉,书又丢在学校,不知道是不是用set,还好最后确实是set,不过set的用法没有掌握,当做vector用了,编译时闹了一堆笑话,最后虽然编译通过,但是样例超时,没交,时间也到了,1000就没看啦。
贴上250代码:
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
class KiwiJuiceEasy{
public:
vector <int> thePouring(vector <int> capacities, vector <int> bottles, vector <int> fromId, vector <int> toId)
{
int m=fromId.size();
int vfrom,vto,idfrom,idto,sizefrom,sizeto;
int mini;
for(int i=0; i<m; i++)
{
idfrom=fromId[i];
idto=toId[i];
sizefrom=capacities[idfrom];
sizeto=capacities[idto];
vfrom=bottles[idfrom];
vto=bottles[idto];
mini=min(vfrom,sizeto-vto);
bottles[idfrom]=bottles[idfrom]-mini;
bottles[idto]=bottles[idto]+mini;
}
return bottles;
}
};
看了别人的代码发现都很短,我的用了过多的中间变量。
总结:第一次,太谨慎了,而且对stl不熟悉,而且比赛开始时还有些紧张。
最后感谢罗伟涛&张珂,中途问了他们好几个小问题呢!
zoj月赛总结
简单说说过程,看是诺亚从前往后看题,铁俊从后往前看题,我从d题开始看,d题最水,我直接推公式,小号wa一次(打错输出字符串)后通过,然后注册队号等事宜,42min过d;
接着铁俊感觉h比较有把握,我让他独自开始,我和诺亚开始攻e题,e是d的扩展,我想了一会儿我大致有一些思路,诺亚和我差不多,然后我写代码,他转和和铁俊一起写h题,我e题只能过样例数据,看到通过率很低,有点困,于是休息了一会儿。铁俊和诺亚的h题写好了,但总是wa,这时我开始攻最后一题,四点左右开始写代码,写到4:20感觉没有太大信心,在加上他们的h题迟迟不能ac,于是半途而废,一起攻h题,我们不断找到新的测试数据并修改中度过了比赛最后一段时间,却总是wa,很遗憾地结束了比赛。
暴露出的不足:
1。代码书写格式问题,(难以理解)缺乏合理注释,变量名随意,数据储存方式粗糙,修改错误代码的方式错误,细节上不够注意
2。浮躁问题,不能够安心编测试数据,急躁
3。缺乏交流,讨论不够热烈
4。比赛策略问题
第一次组队赛总结
今天我们的AC902队正式成立,至于队名的由来,很简单,队里三人住在902宿舍,一个A间,两个C间,正好AC!
12:00到17:00在杭电上办了一场比赛,题目是之前多校联合训练的题目,难度大致与regional相仿,题目稍微少一点。我吃完饭回来正好十二点,开始比赛,简单看了一会儿题目开始做最后一道i牛顿冷却方程题,这题只告诉成正比这个条件,我记得高中时做这个题目时有一个公式的,于是百度开始搜,百度知道的东西不好,完全无法应用(有不少同学就被百度知道坑了,推不出方程),我记得是有更好的方程的,于是继续搜索,终于在豆丁上找到了…^_^…有了公式好办啊,很快写出程序,调试之后通过样例数据,但是交上去总是wa。
这中间,大概12:20的时候,我发现两个队友都还没有到,很奇怪,吃饭花不了这么多时间啊?于是打电话给他们,原来铁俊去火车站买火车票了,方诺亚更远,去下关汽车站买汽车票,两个人一时半会都回不来,我只能孤军奋战!
后来一直查不出I题的错误,开始看剩下几道没有看的题目感觉E题有点希望,h题也可以暴搜+打表,想了一会儿h题发现自己学艺不精,写不出暴搜的代码,比较遗憾。两点左右铁俊回来的,我把最后一题给他讲了一下,希望他能查出错误的代码,然后继续思考e题。三点不到方诺亚也来了,我也正式放弃i题,一起开始研究e题,铁俊那边似乎毫无进展。由于之前对e题有一定思考,所以在方诺亚之前出了思路,然后开始写,写了一会儿,发现他两都趴在桌上睡着了,哈哈,这个也不能怪他们,大热天出去买票的确很累,吼吼,我继续孤军奋战!一会儿,e题代码出来了,调试并且在他们的帮助下改了几个小错,顺利完成测试数据,然后交代码,tle,看看统计,目前尚没有人做对,全是tle,看样子8000ms的题目时间上卡人的确厉害,我用的是数组家sort排序,思路比较一般,肯定会被卡住。
然后看看统计,最后一题做出来的比较多,第一题也有一些,其他题目都是光头。
由于A题博弈题我们都不太会,于是放弃,他两一起开始改e和i的代码,我回过头来重点研究i题,计算精确到小数点后两位都是对的,估计问题不在这里,应在在边界极端数据上,然后自己写几个极端数据调试,果然有问题。于是细心地推理,经过几次失败的尝试,终于ac了,由于一开始孤军奋战时用的是自己的c-lou账号,后来他们回来之后才注册AC902的账号,所以这道理1y,哈哈!
还有一个小时,此时情况基本明朗,过题的队伍i题都过了,部分队伍过了a题,stsky还过了d题,厉害。
时间所剩不多,开新题不大可能,于是我们继续优化e题,先是减少比较次数,奇数直接用中间计算数输出结果,偶数算中间两个较小的一个,还是tle,接着继续优化,发现sort全排序浪费时间,只要一半就可以了,我让他俩找冒泡模版,自己去寻找stl,记过找冒泡模版的说法被鱼头严重鄙视,⊙﹏⊙b。。。果然,被我找到了partial_sort,哈哈,可是,好景不长,无论是他两的一半冒泡还是我的一半partial_sort都出现了runtime error的错误,我估计是小数据的数组越界错误,于是规定小于10用sort ,大于10再用冒泡或partial_sort,可又出现tle,这是让我吗绝望的反馈,意味着这样的优化依然不能解决超时问题,我估计是数据存储方式的问题,时间所剩无几,没有机会再改编数组的存储方式,e题就在wa17次中结束了,占据所有wa次数36的几乎一半。后来看解题报告,果然要用划分树+树状数组来做,这样在排序的时候就能计算和,比我们的方法快了好多。
第一次组队赛就这样结束了,ac了一题,面子上过得去了,自信心也没什么受损,期待明天的zoj月赛能够ac一道题目。
(总结报告竟然写了接近一个小时,1430字,看样子队长不好当啊!O^_^O)
这两天状态不好
在hdu上面刷水题,从2000开始,都是很水的题,不仅很难1y,出现各种小错,而且还总是有查不出来的错误,急死人了。
所以说嘛,自己的基本功还要继续练扎实,输入代码时乱七八糟的小错应该大幅减少,面得动不动一道水题调一个小时。。。。。
为了换换运气,转战UVa,重新找感觉!