21天学C++(五)函数

完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》

五、函数

5.1 声明和定义函数

5.1.1 全局函数

函数不是对象的一部分,可以在程序的任何地方访问。如main()函数。

5.1.2 函数原型

  • int myFunction(int someValue, float someFloat);
  • 返回值类型:任何函数都能返回一个值,有些可能会是空值void,此处返回int;
  • 函数名:函数名字,此处为myFunction;
  • 函数参数(形参):有参数类型呢和参数变量名(可以为空)组成,可是是0个或多个,多个之间用逗号分开;

int theValueReturned = myFunction(5, 7);
传如值(实参):函数参数对应值,数量和类型都需完全相符,如5,7;

5.1.3 函数声明

将函数原型写在头文件里,再用#include将其包含到程序中;

将函数原型写到使用该函数的文件中;

在函数被其他函数调用前定义该函数,将定义作为声明:(不建议)

  • 函数体出现在顺序文件中不明智,需求改变时,很难维护;
  • 某些情况,如函数A调用函数B,函数B调用函数A,这时至少还得先声明一个函数;
  • 使用函数原型,双入口管理,声明和定义一致,调用和声明一致。

5.1.4 函数定义

  • 由函数头和函数体组成,
  • 函数头与函数原型很像,函数参数必须有名称,不以分号结束;
  • 函数头中参数类型必须和函数原型一致,函数名称可以不同;
  • 函数体包含在一对大括号内。

5.1.5 函数返回值

  • 函数须返回一个值或空值;
  • 遇到关键字return后,该函数后续所有语句都不执行;
  • 一个函数内允许有多个return语句
#include <iostream>

int Doubler(int AmountToDouble);

int main()
{
	using std::cout;

	int result = 0;
	int input;

	cout << "Enter a number between 0 and 10,000 to double: ";
	std::cin >> input;

	cout << "\nBefore doubler is called... ";
	cout << "\ninput: " << input << " doubled: " << result << "\n";

	result = Doubler(input);

	cout << "\nBack from Doubler...\n";
	cout << "\ninput: " << input << "   doubled: " << result << "\n";

	return 0;
}

int Doubler(int original)
{
	if (original <= 10000)
		return original * 2;
	else
		return -1;
	std::cout << "You can't get here!\n";
}

执行结果如下:

Enter a number between 0 and 10,000 to double: 100

Before doubler is called...
input: 100 doubled: 0

Back from Doubler...

input: 100   doubled: 200
请按任意键继续. . .

5.1.6 函数执行

5.1.7 建议

  • 注1:函数中不能定义其他函数,但是可以调用其他函数;
  • 注2:函数大小没有显示,但是函数越小越易懂、易维护,建议函数长度大小不超多一屏;
  • 注3:每个函数应该执行一个单一、易懂的任务。
  • 注4:简化调用answer=double(triple(square(cube(value))));

等价于:

int value = 2;
cubed  = cube(value);
squared = square(cubed);
tripled = triple(squared);
answoer = answer(tripled);

5.2 局部、全局变量

5.2.1 局部变量

  • 函数体内声明的变量;
  • 只局限在函数本身,函数返回时,局部变量不再有效,会被释放;
  • 传递给函数的参数也是局部变量;
#include <iostream>

void myFunc();

int main()
{
	int x = 5;
	std::cout << "\nIn main x is: " << x;

	myFunc();

	std::cout << "\nBack in main, x is: " << x;
	return 0;
}

void myFunc()
{
	int x = 8;
	std::cout << "\nIn myFunc, local x: " << x << std::endl;

	{
		std::cout << "\nIn block in myFunc, x is: " << x;

		int x = 9;

		std::cout << "\nVery local x: " << x;
	}

	std::cout << "\nOut of block, in myFunc, x: " << x << std::endl;
}

执行结果如下:


In main x is: 5
In myFunc, local x: 8

In block in myFunc, x is: 8
Very local x: 9
Out of block, in myFunc, x: 8

Back in main, x is: 5请按任意键继续. . .

5.2.2 全局变量

  • 在程序中任何函数均有效,包括main()函数;
  • 声明局部变量和全局变量名字相同时,全局变量的属性不变,局部变量将暂时屏蔽全局变量;
  • 全部变量危险,容易造成数据共享,处处都有可能会误修改全局变量,导致出现错误难以发现;
  • int x = 5, y = 10;
#include <iostream>

using namespace std;   
void myFunction();

int x = 5, y = 10;
int main()
{
	cout << "x: " << x << " y: " << y << endl;
	myFunction();
	cout << "x: " << x << " y: " << y << endl;
	return 0;
}

void myFunction ()
{
	int x = 6, y = 11;
	cout << "x: " << x << " y: " << y << endl;
}

执行结果如下:

x: 5 y: 10
x: 6 y: 11
x: 5 y: 10
请按任意键继续. . .

5.3 默认参数

如果一个函数原型声明了一个参数的默认值,那么调用该函数时,该参数可以不用为其传递值。

  • 限制:如果某个参数没有默认值,那么它前面的所有参数均不得有默认值,即默认值需要从右向左设置。
  • 如:int AreaCube(int length, int width = 25, int height);//错误
  • Height没有设置默认值的情况下,width就不能设置默认值.
#include <iostream>

int AreaCube(int length, int width = 25, int height = 1);

int main()
{
	int length = 100;
	int width = 50;
	int height = 2;
	int area;

	area = AreaCube(length, width, height);
	std::cout << "First area equals: " << area << "\n";

	area = AreaCube(length, width);
	std::cout << "Second time area equals: " << area << "\n";

	area = AreaCube(length);
	std::cout << "Third time area equals: " << area << "\n";
	return 0;
}

int AreaCube(int length, int width, int height)
{
	return (length * width * height);
}

执行结果如下:

First area equals: 10000
Second time area equals: 5000
Third time area equals: 2500
请按任意键继续. . .

5.4 函数重载

5.4.1 函数重载

  • C++允许创建多个名称相同的函数,这些函数通过参数类型,参数个数进行区分,不能通过返回类型区分。
  • 也叫函数的多态性,即多个含义重载一个函数的能力。

5.4.2 用途

通过改变参数的个数或类型对两个或多个函数使用相同的名称。

不用函数重载情况:

#include <iostream>

int Double(int);
long Double(long);
float Double(float);
double Double(double);

using namespace std;

int main()
{
	int      myInt = 6500;
	long     myLong = 65000;
	float    myFloat = 6.5F;
	double   myDouble = 6.5e20;

	int      doubledInt;
	long     doubledLong;
	float    doubledFloat;
	double   doubledDouble;

	cout << "myInt: " << myInt << "\n";
	cout << "myLong: " << myLong << "\n";
	cout << "myFloat: " << myFloat << "\n";
	cout << "myDouble: " << myDouble << "\n";

	doubledInt = Double(myInt);
	doubledLong = Double(myLong);
	doubledFloat = Double(myFloat);
	doubledDouble = Double(myDouble);

	cout << "doubledInt: " << doubledInt << "\n";
	cout << "doubledLong: " << doubledLong << "\n";
	cout << "doubledFloat: " << doubledFloat << "\n";
	cout << "doubledDouble: " << doubledDouble << "\n";

	return 0;
}

int Double(int original)
{
	cout << "In Double(int)\n";
	return 2 * original;
}

long Double(long original)
{
	cout << "In Double(long)\n";
	return 2 * original;
}

float Double(float original)
{
	cout << "In Double(float)\n";
	return 2 * original;
}

double Double(double original)
{
	cout << "In Double(double)\n";
	return 2 * original;
}

执行结果如下:

myInt: 6500
myLong: 65000
myFloat: 6.5
myDouble: 6.5e+020
In Double(int)
In Double(long)
In Double(float)
In Double(double)
doubledInt: 13000
doubledLong: 130000
doubledFloat: 13
doubledDouble: 1.3e+021
请按任意键继续. . .

5.5 内嵌函数

通常函数调用过程中,程序的跳入跳出过程存在着一些影响性能的系统开销。

内嵌函数:关键字inline声明函数。

  • 这样在调用过程中,不需要跳转,直接将函数的代码复制到调用函数中。
  • 由于每次调用,相当于函数复制,可能会使成本大大增加,如一个函数被调用千次、万次,赋值多个会使得可执行程序变大。
  • 建议:如果函数很小,只有一两句语句,而且不会循环调用,这时使用内嵌函数就很恰当。

函数原型:inline int Double(int);
函数的调用方式和普通函数一样。

#include <iostream>

inline int Double(int);

int main()
{
	int target;
	using std::cout;
	using std::cin;
	using std::endl;

	cout << "Enter a number to work with: ";
	cin >> target;
	cout << "\n";

	target = Double(target);
	cout << "Target: " << target << endl;

	target = Double(target);
	cout << "Target: " << target << endl;

	target = Double(target);
	cout << "Target: " << target << endl;
	return 0;
}

int Double(int target)
{
	return 2*target;
}

执行结果如下:

Enter a number to work with: 5

Target: 10
Target: 20
Target: 40
请按任意键继续. . .

5.6 递归

5.6.1 递归:函数调用本身

使用环境:当对某些数据做某种处理,而后又对其结果做同样处理的情况下使用。

递归导致两种结果:一种是程序退出并得处答案,另一种是程序陷入死循环。

  • 函数调用本身时,系统会在内存中为该函数创建一个新备份,新备份中的局部变量独立于前一个备份中的局部变量。
  • 使用时慎用,过多的备份容易将内存耗尽。
#include <iostream>
int fib (int n);

int main()
{

	int n, answer;
	std::cout << "Enter number to find: ";
	std::cin >> n;

	std::cout << "\n\n";

	answer = fib(n);

	std::cout << answer << " is the " << n;
	std::cout << "th Fibonacci number\n";
	return 0;
}

int fib (int n)
{
	std::cout << "Processing fib(" << n << ")... ";

	if (n < 3 )
	{
		std::cout << "Return 1!\n";
		return (1);
	}
	else
	{
		std::cout << "Call fib(" << n-2 << ") ";
		std::cout << "and fib(" << n-1 << ").\n";
		return( fib(n-2) + fib(n-1));
	}
}

执行结果如下:

Enter number to find: 5


Processing fib(5)... Call fib(3) and fib(4).
Processing fib(3)... Call fib(1) and fib(2).
Processing fib(1)... Return 1!
Processing fib(2)... Return 1!
Processing fib(4)... Call fib(2) and fib(3).
Processing fib(2)... Return 1!
Processing fib(3)... Call fib(1) and fib(2).
Processing fib(1)... Return 1!
Processing fib(2)... Return 1!
5 is the 5th Fibonacci number
请按任意键继续. . .

5.6.2 使用递归调用过程

5.6.3 递归的返回过程

5.7 函数的调用过程(可选)

5.7.1 C++划分内存

5个区:堆、栈、自由存储区、全局/静态存储区、常量存储区。

  • 栈:编译器按需分配和清除,通常存放局部变量、函数参数等;
  • 堆:由程序员申请new和释放delete,如果没有释放掉,程序结束后,操作系统会自动回收。 (new和delete需要配对使用,否则容易造成内存泄露);
  • 自由存储区:由malloc等分配的内存块,用fress释放,和堆是十分相似的。 
  • 全局/静态存储区:存放全局变量和静态变量。 
  • 常量存储区:存放常量,不允许修改。

5.7.2 堆和栈区别

在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中;栈地址则由寄存器管理。

发表回复