自学编程,从此开始

上第2学堂,听有趣的编程课

课文: 《你好,函数!》 (点击查看完整内容:视频+评测+讨论+……)

作者:null

丁小明遇到一位同事,就问候对方的名字……绝不能一行一行地重复“cout”,那太累了……

课文题图

 

第3节:你好,函数!

代码演化

先将三个版本的问候代码贴出来:

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello !" << endl;
    cout << "Hello !" << endl;
    cout << "Hello !" << endl;
    cout << "Hello !" << endl; 
    cout << "Hello !" << endl;

    return 0;
}
#include <iostream>

using namespace std;

void Hello()
{
    cout << "Hello !" << endl;
}

int main()
{
    Hello();
    Hello();
    Hello();
    Hello();
    Hello();

    return 0;
}

版本二的另一个小变化是将“Hello”函数中的“Hello !”改为中文“你好!”。这体现了函数面对非结构性变化时,较好维护一面:

#include <iostream>

using namespace std;

void Hello()
{
    cout << "你好!" << endl;
}

int main()
{
    Hello();
    Hello();
    Hello();
    Hello();
    Hello();

    return 0;
}
#include <iostream>
#include <string> //For 人名使用标准库的字符串类型

using namespace std;

void Hello(string name)
{
    cout << "你好," << name << "!" << endl;
}

int main()
{
    Hello("林总");
    Hello("张爱风");
    Hello("南老师");
    Hello("林字玲");
    Hello("涂二S");

    return 0;
}

Hello()函数在定义时所写的参数“string name”和Hello()函数在调用时所使用的数据“林总”等,都被泛称为函数参数,但二者一虚一实,具体区别会在下一节课讲解“数据”时谈及。今天的重点是理解函数定义与函数调用的区别与关联。

函数参数:带来变化的因子

如果说函数是代表“做一件事”,那么请理解其背后所体现的“做事”的变与不变。做事大的流程、步骤是一样的,但每次做事可能会有一些变化因素。比如,未使用参数之前的问候,就没有体现变化:对每个同事都是在问一句“你好!”;而加入name参数后,不变仍然存在,仍然在对各位同事问候“你好”,但变化也产生了:针对不同的同事,问候不同的名字。由此就能理解:参数是让一件大体不变的做事过程,带上每一次调用时都可以存在的变化因素。参数就是带来此类变化的因素(因子)。

理解函数返回结果的定义

在定义函数时,对函数结果的描述,是使用类型,而不是真实的数据。这是因为函数定义时,并还没有被实际执行(调用),也就是说函数所代表的动作还没动起来呢,怎么会有真实的结果数据?只能做类型这样的描述(定义):“哦,这个函数将返回这个样子/这种类型的数据”。比如,定义吃饭函数,只能说明这个函数会返回饱或饱(通常使用真或假对应)这样类型的数据;要在实际吃饭之后,才能返回真的饱或不饱的状态。

“函数定义时,函数并没有真正执行”,这个道理也适用于函数的参数定义。此时并没有真的数据到来,只能定义说:当函数执行起来,这里会有这么一种类型的数据。不过,相比结果数据描述,函数的参数定义是既有数据类型又有数据名字的,比如:

void Hello(string name) //string:类型,name:名字

这是因为,在函数内部,通常我们需要用到这个数据,所以它需要有个名字,不然我们如何称呼它?有关数据的定义,会在下一节《你好,数据(上)》讲得更透彻。

借助函数返回值以及函数参数需要说明类型,我们提一下感受篇前面的课节中,常常需要用到的一些常见的数据类型:

主函数

视频中提到,函数有四个组成部分:函数名、返回类型、参数表、函数体。阅读代码,不难发现:main就是一个函数:

int main()
{
    Hello();
    ……
    return 0;
}

名字:main、返回整数类型、参数表为空,函数体则是连续5次调用Hello()函数。

注意,main()函数 调用了Hello(),若将来有需要,我们可以再定义一个函数 ,比如叫“foo()”,然后Hello()函数内调用foo()。事情就是这样一级一级往下走,并逐级化解,大事化小,小事化了……直到使用简单语句就能解决。问题来了:地球最初是怎么转动起来的?据牛顿说,是上帝之手推了一把……main()是谁调用的?整个代码中有调用main()的代码吗?大家找找?没有。

可以将程序理解成一个大函数。程序写完,哪怕编译完之后,都和函数定义完一个意思,它们是“静止”的。想要动起来,需要有上帝之手。上帝就是坐在电脑前的,程序的用户。是他通过鼠标或键盘或语音命令或者是触摸屏上的指头,告诉操作系统 :“我想运行这个程序”,于是操作系统才去启动程序,而在启动时,操作系统需要找到一个入口。这个入口通常就是满足纯C语言定义的某个函数。不同的操作系统对这个函数的名字、参数会有不同的约定,比如Windows下,要求这个函数名叫“WinMain(……)”;但在C/C++语言这边,内部约定是叫“主”,也就是“main”名字的函数,在编译时,编译器会根据需要,将它转换成操作系统实际所需要的入口函数。“main()”因为翻译原因,常被称为“主函数”,另一种更形象的说法,是程序的“入口函数”。

主函数不仅对名字有要求,对返回值也有要求,必须是int类型。main函数若返回0,是来告诉调用者(也就是操作系统)本次运行一切正常;若返回非零(通常是负数),则表示本次运行中出了些差错。

主函数对参数表也有些要求,在C/C++中,第一种合法的入口参数表就是为空,正是前面例中展现的形式。另一种是:

int main(int argc, char * avg[])
{

}

两个参数用来接受外部调用程序时传入的一些变化因素,具体含义以后再说。

最后,主函数还有个特权:可以不写 return语句。按说,既然规定返回类型是int,那就得有个return XXX; 语句存在,其中XXX的值是整数。对其它函数来说,确实如此,main()却有个特权,如果不写这个return语句,就默认表示返回零,也就是表示说:本次运行一切OK。习惯报喜不报忧,原来C++的主函数也有这脾气。