自学编程,从此开始

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

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

作者:null

丁小明遇到一位同学,然后尴尬地发现自己叫不出她的名字,怎么办?

课文题图

 

第4节:你好,数据(上)

形参、实参中的数据

参数带个“数”字,并且它正好也确实是数据。不过,当我们说参数(函数的参数)时,通常模糊了两种情况下的参数的区别。

一者是函数定义时的形式参数。函数定义时,参数表中的数据,有类型、有名字,比如:


此时,参数是在说明:如果要问好,需要一个数据,这个数据的类型必须是string,而这个数据的名字,让我们假设它是叫“name”吧,这个参数被称为“形参”,即“形式参数”。请理解这中间的“必须是”和“假设是”。没错,数据是什么类型比较重要,至于是叫什么名字,不算重要;而至于它的内容会是什么?不是不重要,而是在函数调用之前,确实不知道啊

以上那段上话,就谈到数据的三个要素:内容(值)、类型、名字。

接着,到了函数调用的时候,在本例中,我们直接将数据的值写在代码中,作为Hello()函数的实际参数传入:

Hello("林总");
Hello("张爱风");

每次调用都传入当前遇到的同事的名字。这里的名字“林总”或“张爱风”,是真实的数据,称为“实参”,即“实际参数”。当然,有人非说“林总”又不是真的林总,而只是一个字符串,所以怎么能叫“实际”参数呢?这这这……我们在启蒙篇时就讲过,学编程要讲逻辑啊!!不带这么胡搅蛮缠的。我们说的“实际”,是指这两个数据都占用了实际内存,它们其实都有内存地址——我们曾经偷窥过,还记得吧!只不过当时偷窥的是“王语嫣”和“任盈盈”(当然,刚才那好同学说得真对:就名字而已)。

那么,形参是不是真的就完全只是“形式”上的东西呢?是纯“形式”或不是,要看它是否对应到内存实体。当函数被调用时,形参需要真正占用一块内存,用来接受从函数调用那边传过来的实参。正如视频中展现的那两辆小货车。这中间的传递过程有“传值”和“传址”两种方式。

不管是字符串的完整内容,还是传的是“写在纸条”上的一个地址,想要传给Hello()函数,后者还是需要准备好一块内存空间用来“接”住数据(内容或“纸条”)。如果仍以快递比喻,那么负责传递参数的快递员,在货物送达函数门前时,并不敲门。一个带参的函数,必须在家门口准备一个箱子,因为快递员需要把送来的东西放在这个箱子内。但有意思的是,函数又不能一直将这体箱子放在门口——因为那样太占地儿——必须在函数调用的那一刻,迅速准备好这个箱子,等到函数调用结束(就是函数执行完了),就收回箱子。事实上,函数返回结果也是类似这样的过程,只不过返回结果是由函数自己把结果数据放在这个临时的箱子,在调用完成后,这个箱子也会被收起来。

这么一分析,形参其实也是——严格地说,在有可能曾经是真实的数据,因为它占用过内存,来过,爱过,死过。

视频里都强调了:传值和传址虽然重要,但并不是本课重点,我们讲它们的目的,是为了突显数据的另一个重要的核心要素:址。即数据在内存中的地址。现在,数据有四个要素了:名字、值 、地址、类型。

cin、 >> 、getline

c-in和c-out在字面上,一看就是相对的。前者输入,后者输出。后者我们很熟悉,常和“<<”结合使用:

cout << "张三丰" << endl;

负责将<<后面的数据(所占用的内存中的内容),输出到cout身上去,而cout代表标准输出设备,通常就是屏幕。

对应的,cin代表标准输入设备,通常就是键盘。而它和>>结合,可以将键盘输入的内容存放到后面数据(所占用或将来继续分配的内存)中去。比如:

string name; //一个数据
cin >> name;

name在例中用来表示姓名,中国人的姓名中间不会有空格,但老外名加姓是多个单词,单词之间通常会用空格区分。这时“>>”就有瑕疵了,它会在读取中空格时就自动结束……比如,如果用户输入“约翰 冯 诺依曼”——天啊,丁小明竟然在公司里碰上祖师爷了——丁小很亲昵地称呼祖师爷为“约翰”。或许这并不是他的本意。解决方法一是自己通过循环等,写个复杂流程,但丁小明还没习过流程控制。合理而方便的方法是使用标准库的getline函数,get-line,从名字就能猜出,它是负责读入一行数据的。

string name;
getline(cin, name);

getline函数第一个参数必须是输入流,这个概念现在的理解就是它像水流一样,会不断地往某个池子灌水。cin就是这样一个代表从标准输入设备(通常是键盘)不断流入数据的流(对应的,cout叫输出流,今天,我们对cout和cin的认识,终于又向前了一步)。

变量

值允许变,并且通常会有个名字的数据,叫变量。定义函数是产生一个函数。定义一个变量,就是在对应的代码,将来会在程序运行时,在内存中占用一块特定大小的内存,并且拥有一个地址(占用内存就必然的占用一个内存的地址)。在占用内存时,就往内存中写入的内容,叫这个数据的初值(有时也称初始值);而在定义时为它设置初值的这个代码行为,叫“初始化”。

视频只讲了一种,但其实C++有很多初始化的方法。唉!比如:

int a {10};
int a (10);

没错,花括号在这里不是代表一个复合语句了。它可以用作数据的初始化。圆括号也不用来做函数调用了,它也可用作数据的初始化。通常现代的C++(指C++ 2011年标准),都推荐使用 上述的 花括号的形式。感受篇出于让代码更加符合我们的生活经验(主要是数学),所以使用‘= 初值’的形式。

Hello Interaction 完整代码

#include <iostream>
#include <string> //For 姓名

using namepsace std;

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

}

//问对方名字
string WhatIsYourName()
{
    cout << "您好,我是丁小明,您是?" << endl;
    cout << "我是:";

    string name;
    cin >> name; //从键盘得到输入内容,如果要兼容名字中间的空格,可以使用 getline(cin, name);

    return name;
}

int main()
{
    Hello("林总");
    Hello("张大师");

    string name = WhatIsYourName();
    Hello(name);

    name = WhatIsYourName(); //再变一次
    Hello(name);

    return 0;
}

作业

1、继续优化WhatIsYourName中的交互过程,让程序输出在整体上更合理。

2、在代码中试用getline函数,并输入带空格的字符串内容作为名字加以测试。