• 【C++学习第四讲】函数入门


    一、前言

    由于函数用于创建C++程序的模块,对C++的OOP(面向对象程序设计)定义至关重要,因此必须熟悉它。

    C++函数分两种:有返回值的和没有返回值的。

    在标准C++函数库中可以找到这两类函数的例子,您也可以自己创建这两种类型的函数。

    下面首先来看一个有返回值的库函数,然后介绍如何编写简单的函数。

    二、使用有返回值的函数

    有返回值的函数将生成一个值,而这个值可赋给变量或在其他表达式中使用。

    例如,标准C/C++库包含一个名为sqrt( )的函数,它返回平方根。假设要计算6.25的平方根,并将这个值赋给变量x,则可以在程序中使用下面的语句:

    x = sqrt(6.25);
    
    • 1

    表达式sqrt(6.25)将调用sqrt( )函数。表达式sqrt(6.25)被称为函数调用,被调用的函数叫做被调用函数(called function),包含函数调用的函数叫做调用函数(calling function)。

    圆括号中的值(这里为6.25)是发送给函数的信息,这被称为传递给函数。以这种方式发送给函数的值叫做参数。

    函数sqrt( )得到的结果为2.5,并将这个值发送给调用函数;发送回去的值叫做函数的返回值(return value)。

    可以这么认为,函数执行完毕后,语句中的函数调用部分将被替换为返回的值。因此,这个例子将返回值赋给变量x。

    简而言之:

    • 参数是发送给函数的信息
    • 返回值是从函数中发送回去的值

    调用函数的示意图如下:

    在这里插入图片描述

    函数调用的句法如下:

    在这里插入图片描述

    情况基本上就是这样,只是在使用函数之前,C++编译器必须知道函数的参数类型和返回值类型。

    也就是说,函数是返回整数、字符、小数、有罪裁决还是别的什么东西?如果缺少这些信息,编译器将不知道如何解释返回值。C++提供这种信息的方式是使用函数原型语句。

    C++程序应当为程序中使用的每个函数提供原型。

    函数原型之于函数就像变量声明之于变量—指出涉及的类型。

    例如,C++库将sqrt( )函数定义成将一个(可能)带小数部分的数字(如6.25)作为参数,并返回一个相同类型的数字。有些语言将这种数字称为实数,但是C++将这种类型称为double。

    sqrt( )的函数原型像这样:

    double sqrt(double);
    
    • 1

    第一个double意味着sqrt( )将返回一个double值。括号中的double意味着sqrt( )需要一个double参数。因此该原型对sqrt( )的描述和下面代码中使用的函数相同:

    double x;
    x = sqrt(6.25);
    
    • 1
    • 2

    原型结尾的分号表明它是一条语句,这使得它是一个原型,而不是函数头。如果省略分号,编译器将把这行代码解释为一个函数头,并要求接着提供定义该函数的函数体。

    在程序中使用sqrt( )时,也必须提供原型。可以用两种方法来实现:

    1. 在源代码文件中输入函数原型

    2. 包含头文件cmath(老系统为math.h),其中定义了原型

    第二种方法更好,因为头文件更有可能使原型正确。对于C++库中的每个函数,都在一个或多个头文件中提供了其原型。请通过手册或在线帮助查看函数描述来确定应使用哪个头文件。例如,sqrt( )函数的说明将指出,应使用cmath头文件。(同样,可能必须使用老式的头文件math.h,它可用于C和C++程序中。)

    不要混淆函数原型和函数定义。

    可以看出,原型只描述函数接口。也就是说,它描述的是发送给函数的信息和返回的信息。而定义中包含了函数的代码,如计算平方根的代码。C和C++将库函数的这两项特性(原型和定义)分开了。库文件中包含了函数的编译代码,而头文件中则包含了原型。

    应在首次使用函数之前提供其原型。通常的做法是把原型放到main( )函数定义的前面。

    下面的演示了库函数sqrt( )的用法,它通过包含cmath文件来提供该函数的原型:

    #include
    #include
    int main() {
    	using namespace std;
    	double x;
    	cout << "请输出一个数字:";
    	cin >> x;
    	double y;
    	y = sqrt(x);
    	cout << "y的值为:" << y << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    如果使用的是老式编译器,则必须在程序中使用#include ,而不是#include。

    C++库函数存储在库文件中。编译器编译程序时,它必须在库文件搜索您使用的函数。至于自动搜索哪些库文件,将因编译器而异。如果运行程序清单2.4时,将得到一条消息,指出_sqrt是一个没有定义的外部函数(似乎应当避免),则很可能是由于编译器不能自动搜索数学库(编译器倾向于给函数名添加下划线前缀—提示它们对程序具有最后的发言权)。

    下面是该程序的运行情况:

    请输出一个数字:12
    y的值为:3.4641
    
    D:\C++Project\Project1\x64\Debug\Project1.exe (进程 47948)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    由于sqrt( )处理的是double值,因此这里将变量声明为这种类型。声明double变量的句法与声明int变量相同:
    在这里插入图片描述
    double类型使得变量x和y能够存储带小数的值,如1 536.0和39.191 8。将看起来是整数(如1536)的值赋给double变量时,将以实数形式存储它,其中的小数部分为.0。double类型覆盖
    的范围要比int类型大得多。

    C++允许在程序的任何地方声明新变量,因此sqrt.cpp在要使用y时才声明它。C++还允许在创建变量时对它进行赋值,因此也可以这样做:

    double y = sqrt(x);
    
    • 1

    这个过程叫做初始化(initialization)。后面会详细讲解。

    cin知道如何将输入流中的信息转换为double类型,cout知道如何将double类型插入到输出流中。前面讲过,这些对象都很智能化。

    三、函数变体

    有些函数需要多项信息。这些函数使用多个参数,参数间用逗号分开。

    例如,数学函数pow( )接受两个参数,返回值为以第一个参数为底,第二个参数为指数的幂。该函数的原型如下:

    double pow(double, souble);
    
    • 1

    要计算5的8次方,可以这样使用该函数:

    output = pow(5.0, 8.0);
    
    • 1

    另外一些函数不接受任何参数。

    例如,有一个C库(与cstdlib或stdlib.h头文件相关的库)包含一个rand( )函数,该函数不接受任何参数,并返回一个随机整数。该函数的原型如下:

    int rand(void);
    
    • 1

    关键字void明确指出,该函数不接受任何参数。

    如果省略void,让括号为空,则C++将其解释为一个不接受任何参数的隐式声明。可以这样使用该函数:

    a = rand();
    
    • 1

    注意,与其他一些计算机语言不同,在C++中,函数调用中必须包括括号,即使没有参数。

    还有一些函数没有返回值。例如,假设编写了一个函数,它按美元、美分格式显示数字。当向它传递参数23.5时,它将在屏幕上显示$23.50。由于这个函数把值发送给屏幕,而不是调用程序,因此不需要返回值。可以在原型中使用关键字void来指定返回类型,以指出函数没有返回值:

    void bucks(double);
    
    • 1

    由于它不返回值,因此不能将该函数调用放在赋值语句或其他表达式中。相反,应使用一条纯粹的函数调用语句:

    bucks(23.50);
    
    • 1

    在有些语言中,有返回值的函数被称为函数(function);没有返回值的函数被称为过程(procedure)或子程序(subroutine)。但C++与C一样,这两种变体都被称为函数。

    四、用户定义的函数

    4.1 引入

    标准C库提供了140多个预定义的函数。如果其中的函数能满足要求,则应使用它们。但用户经常需要编写自己的函数,尤其是在设计类的时候。无论如何,设计自己的函数很有意思,下面来介绍这一过程。

    前面已经使用过好几个用户定义的函数,它们都叫main( )。每个C++程序都必须有一个main( )函数,用户必须对它进行定义。假设需要添加另一个用户定义的函数。和库函数一样,也可以通过函数名来调用用户定义的函数。

    对于库函数,在使用之前必须提供其原型,通常把原型放到main( )定义之前。但现在您必须提供新函数的源代码。最简单的方法是,将代码放在main( )的后面。

    #include
    void s(int);
    int main() {
    	using namespace std;
    	s(3);
    	cout << "请输入一个数字:";
    	int count;
    	cin >> count;
    	s(count);
    	cout << "完成!" << endl;
    	return 0;
    }
    void s(int n) {
    	using namespace std;
    	cout << "数字为:" << n << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    main( )函数两次调用s( )函数,一次的参数为3,另一次的参数为变量count。

    在这两次调用之间,用户输入一个整数,用来设置count的值。这个例子没有在cout提示消息中使用换行符。这样将导致用户输入与提示出现在同一行中。下面是运行情况:

    数字为:3
    请输入一个数字:10
    数字为:10
    完成!
    
    D:\C++Project\Project1\x64\Debug\Project1.exe (进程 45188)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.2 函数格式

    s( )函数的定义与main( )的定义采用的格式相同。首先,有一个函数头;然后是花括号中的函数体。可以把函数的格式统一为如下的情形:

    在这里插入图片描述
    注意,定义s( )的源代码位于main( )的后面。和C一样,C++不允许将函数定义嵌套在另一个函数定义中。每个函数定义都是独立的,所有函数的创建都是平等的。

    4.3 函数头

    s()函数的函数头如下:

    void s(int n)
    
    • 1

    开头的void表明s( )没有返回值,因此调用s( )不会生成可在main( )中将其赋给变量的数字。因此,第一个函数调用方式如下:

    s(3);
    
    • 1

    由于s( )没有返回值,因此不能这样使用它:

    output = s(3);
    
    • 1

    函数定义在文件中依次出现:

    在这里插入图片描述

    括号中的int n表明,使用s( )时,应提供一个int参数。n是一个新的变量,函数调用时传递的值将被赋给它。因此,下面的函数调用将3赋给s( )函数头中定义的变量n:

    s(3);
    
    • 1

    当函数体中的cout语句使用n时,将使用函数调用时传递的值。这就是为什么s(3)在输出中显示3的原因所在。

    简而言之,s( )的函数头表明,该函数接受一个int参数,不返回任何值。

    下面复习一下main( )的函数头:

    int main()
    
    • 1

    开头的int表明,main( )返回一个整数值;空括号(其中可以包含void)表明,main( )没有参数。对于有返回值的函数,应使用关键字return来提供返回值,并结束函数。这就是为什么要在main( )结尾使用下述语句的原因:

    return 0;
    
    • 1

    这在逻辑上是一致的:main( )返回一个int值,而程序员要求它返回整数0。但可能会产生疑问,将这个值返回到哪里了呢?毕竟,程序中没有哪个地方可以看出对main( )的调用:

    output = main();
    
    • 1

    答案是,可以将计算机操作系统(如UNIX或Windows)看作调用程序。

    因此,main( )的返回值并不是返回给程序的其他部分,而是返回给操作系统。

    很多操作系统都可以使用程序的返回值。

    例如,UNIX外壳脚本和Windows命令行批处理文件都被设计成运行程序,并测试它们的返回值(通常叫做退出值)。

    通常的约定是,退出值为0则意味着程序运行成功,为非零则意味着存在问题。因此,如果C++程序无法打开文件,可以将它设计为返回一个非零值。然后,便可以设计一个外壳脚本或批处理文件来运行该程序,如果该程序发出指示失败的消息,则采取其他措施。

    五、用户定义的有返回值的函数

    我们再深入一步,编写一个使用返回语句的函数。

    main( )函数已经揭示了有返回值的函数的格式:在函数头中指出返回类型,在函数体结尾处使用return。

    可以用这种形式为在英国观光的人解决重量的问题。在英国,很多浴室都以英石(stone)为单位,不像美国以磅或公斤为单位。一英石等于14磅,下面的示例程序使用一个函数来完成这样的转换。

    #include
    int stone(int);
    int main() {
    	using namespace std;
    	int s;
    	cout << "请输入一个整数:";
    	cin >> s;
    	int puns = stone(s);
    	cout << s << " s = ";
    	cout << puns << " puns ";
    	return 0;
    }
    int stone(int n) {
    	return 14 * n;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    下面是程序的运行结果:

    请输入一个整数:5
    5 s = 70 puns
    D:\C++Project\Project1\x64\Debug\Project1.exe (进程 24544)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在main( )中,程序使用cin来给整型变量s提供一个值。这个值被作为参数传递给stone( )函数,在该函数中,这个值被赋给变量n。然后,stone( )用关键字return将14*n返回给main( )。这表明
    return后面并非一定得跟一个简单的数字。这里通过使用较为复杂的表达式,避免了创建一个新变量,将结果赋给该变量,然后将它返回。程序将计算表达式的值,并将其返回。如果返回表达式的值很麻烦,可以采取更复杂的方式:

    int stone(int n) {
    	int pubs = 14 * n;
    	return pubs;
    }
    
    • 1
    • 2
    • 3
    • 4

    这两个版本返回的结果相同,但第二个版本更容易理解和修改,因为它将计算和返回分开了。

    通常,在可以使用一个简单常量的地方,都可以使用一个返回值类型与该常量相同的函数。

    这些例子表明,函数原型描述了函数接口,即函数如何与程序的其他部分交互。参数列表指出了何种信息将被传递给函数,函数类型指出了返回值的类型。程序员有时将函数比作一个由出入它们的信息所指定的黑盒子(black boxes)(电工用语)。函数原型将这种观点诠释得淋漓尽致:

    在这里插入图片描述
    函数stone( )短小、简单,但包含了全部的函数特性:

    • 有函数头和函数体
    • 接受一个参数
    • 返回一个值
    • 需要一个原型

    可以把stone( )看作函数设计的标准格式。

    六、 在多函数程序中使用using编译指令

    在上面的程序设计中,两个函数中都包含下面一条using编译指令:

    using namespace std;
    
    • 1

    这是因为每个函数都使用了cout,因此需要能够访问位于名称空间std中的cout定义。

    在程序中,可以采用另一种方法让两个函数都能够访问名称空间std,即将编译指令放在函数的外面,且位于两个函数的前面:

    #include
    using namespace std;
    void s(int);
    int main() {
    	s(3);
    	cout << "请输入一个数字:";
    	int count;
    	cin >> count;
    	s(count);
    	cout << "完成!" << endl;
    	return 0;
    }
    void s(int n) {
    	cout << "数字为:" << n << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    输出结果为:

    数字为:3
    请输入一个数字:12
    数字为:12
    完成!
    
    D:\C++Project\Project1\x64\Debug\Project1.exe (进程 19948)已退出,代码为 0。
    要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
    按任意键关闭此窗口. . .
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当前通行的理念是,只让需要访问名称空间std的函数访问它是更好的选择。

    总之,让程序能够访问名称空间std的方法有多种,下面是其中的4种:

    1. using namespace std;放在函数定义之前,让文件中所有的函数都能够使用名称空间std中所有的元素。

    2. using namespace std;放在特定的函数定义中,让该函数能够使用名称空间std中的所有元素。

    3. 在特定的函数中使用类似using std::cout;这样的编译指令,而不是using namespace std;,让该函数能够使用指定的元素,如cout。

    4. 完全不使用编译指令using,而在需要使用名称空间std中的元素时,使用前缀std::,如下所示:
      在这里插入图片描述

    七、命名约定

    C++程序员给函数、类和变量命名时,可以有很多种选择。程序员对风格的观点五花八门,这些看法有时就像公共论坛上的圣战。就函数名称而言,程序员有以下选择:

    Myfunction( )
    myfunction( )
    myFunction( )
    my_function( )
    my_funct( )
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    休闲娱乐APP,你是否感兴趣呢
    JVM-垃圾回收
    CC攻击演示
    java八股文面试题
    讲解神经网络的书籍,讲解神经网络的软件
    为什么 NGINX 的 reload 不是热加载?
    工控上位机程序为什么只能用C语言?
    一文聊透 Netty 核心引擎 Reactor 的运转架构
    初入社会的测试如何去判断一个公司值不值得继续待下去?
    前后端分离(SpringBoot)
  • 原文地址:https://blog.csdn.net/wzk4869/article/details/127815815