c++函数知识点小结(详解函数参数和返回值的工作原理)

函数与数组在某些方面很类似:同样占据一段内存空间,其名字同样代表其占用内存空间的首地址。但运行时调用函数可以执行语句,而数组则不行。
因此将函数当作一个整体看(通常是作为函数名存在)时,可以将其看作为一个类似数组的变量,类比理解;而调用函数时,则将函数拆散来看,关注函数对函数体内变量的操作
以下的分析全都基于这一理解。

函数名的四种含义

函数名的4种含义
看函数名后面是否有圆括号
看函数最后是否有分号
函数的地址
看函数名前是否有显式的类型
函数的声明
参数的个数和类型
返回的类型
函数的调用
函数的定义
进入函数并执行函数内的语句
相当于黑箱
具体描述了函数的动作

作用域和生命周期

二者的区别

  • 作用域是指某变量在代码块中可视范围
  • 生命周期是指某变量在运行时存在的一段时间
#include <iostream>
using std::cout;
using std::endl;

int i;
void func()
{
	static int m;
	cout << m << endl;
	m = 5;
}

int main()
{
	int j;
	{
		int k;
	}
	func();
	func();
}

在这段代码,i、func在整个代码段中可视,m在func函数中可视,j在main函数中可视,k在main的内层大括号中可视;变量m在运行过程中一直存在,且只在第一次定义时自动初始化为0,而第二次输出的m是在第一次调用func函数后赋值为5的结果,故输出结果如下:
在这里插入图片描述

传递参数

传参的工作原理

  • 无论参数的数据类型是什么,传参都是传入参数的
#include <iostream>
using std::cout;
using std::endl;

int f(int i)
{
	return 0;
} 

int main()
{
	int j;
	j = f(7);
}

如上代码,调用f函数的过程即

int j;
int i = 7;
j = 0;

解释:

  1. 定义int型变量j
  2. 定义int型变量i,并将7赋值给i
  3. 将返回的0值赋值给j

当形参是指针或引用时,调用函数的原理也同上

#include <iostream>
using std::cout;
using std::endl;

void f(int &i)
{
	i = 5;
	return;
} 

int main()
{
	int j = 0;
	f(j);
	cout << j << endl; 
}

如上代码,调用f函数的过程即:

int j = 0;
int &i = j;
i = 5;
cout << j << endl; 

解释:

  1. 定义int型变量j
  2. 定义int型引用变量i,并将i与j绑定
  3. 对i赋值为5(即对j赋值为5)
  4. 输出j的值

输出结果如下
在这里插入图片描述

默认实参

合法的默认实参

声明时:有默认实参的形参右边的参数都应有默认实参。
调用时:有具体值的实参左边的实参都应有具体的值。

/*函数的声明*/
void f(int, int, int i = 5);//正确
void f(int, int i = 5, int);//错误
void f(int, int i = 5, int j = 5);//正确

/*函数的调用*/
int main()
{
   ···
   f(5, 5, 5);//正确
   f(5);//当后两个位置有默认实参时正确
   f(5, , 5);//无论中间位置是否有默认实参,此调用方法均不正确
   ···
}

注意:逗号运算符的左侧须有primary-expression,因此后方位置想使用默认实参时需省略","

有默认实参情况下对函数传参过程的理解

定义的形参被默认实参初始化后,再用传入的值对各个形参进行赋值
先上代码:

#include <iostream>
using std::cout;
using std::endl;

void f(int i, int j = 4, int k = 5)
{
	cout << i << " " << j << " " << k << endl;
} 

int main()
{
	f(1, 2);
	f(1);
}

输出结果如下:
在这里插入图片描述第一次进入f函数时,发生了以下过程:

int i, j = 4, k = 5;//被默认实参初始化
i = 1, j = 2;//用实参进行赋值
cout << i << " " << j << " " << k << endl;

而第二次进入f函数时:

int i, j = 4, k = 5;//被默认实参初始化
i = 1;//用实参进行赋值
cout << i << " " << j << " " << k << endl;

return的工作原理

void类

返回void函数不要求非得有return语句,因为在此类函数的最后一句后面会隐式地执行return。 ——《c++ primer》

非void类

从执行return语句开始,被调用函数中定义的局部自动变量释放内存空间,return后的表达式的值(可能是具体值,也可能是地址)被拷贝到调用点。
注意:当返回类型是指针或引用时必须返回调用点可视范围内的变量

返回值是指针

int j;
int *f(int i)
{
	return &i;//错误,返回时i被释放了,在main函数中无法检测到此变量
	return &j;//正确
} 

int main()
{
	int m;
	int *ptr = f(m);
}

返回值是引用

调用一个返回引用的函数得到左值。——《c++ primer》

先上代码:

#include <iostream>
using std::cout;
using std::endl;

int &f(int &i)
{
	return i;
} 

int main()
{
	int m = 0;
	int n = 1;
	f(m) = n;
	if(m)
		cout << "n对m的引用赋值" << endl; 
	else
		cout << "n未对m的引用赋值" << endl; 
}

结果如下:

在这里插入图片描述
调用时,i是int类型的引用,与m绑定在一起,因此返回i的引用即相当于返回m的引用。返回调用点后,用n的值对m的引用进行了赋值。
此过程相当于:

/*在main函数代码段中*/
int m = 0;
int n = 1;
/*在f函数代码段中*/
int &i = m;
return i;//即return了m
/*在main函数代码段中*/
m = n;//用n的值对返回的m的别名(也就是m)赋值
if(m)
	cout << "n对m的引用赋值" << endl; 
else
	cout << "n未对m的引用赋值" << endl; 

函数指针

函数会占用内存空间的一段地址。
数组名代表数组首元素所在的首地址,类似的,函数名也代表函数所占用内存空间的首地址

函数名作为参数

函数表现形式代表意义
单独的函数名函数所占内存空间的首地址
函数名+形参列表函数的定义/声明(看后头跟什么)
函数名+实参列表调用函数后返回的值

当函数名单独存在作为参数(包括形参和实参)时,会自动转换成指向函数首地址的指针(这一点与数组类似)。

函数重载

定义

同一作用域内几个函数名字相同但形参列表不同,我们称之为函数重载。——《c++ primer》

合法的重载

  1. 重载函数在形参数量或形参类型上有所不同
  2. 同一类型的不同别名不算形参类型不同
  3. 是否加顶层const不影响形参的类型

调用的顺序

  1. 实参类型与形参相同或数组/函数名转换为对应的指针类型
    其中,顶层const不改变数据类型,因此顶层const实参对应无const形参也属于同一类型
  2. 底层const转换
  3. 通过类型提升的转换:int->double, short->int之类
  4. ①算术类型转换:5.2 + 3->double
    ②指针转换:
指针原样转换目标
nullptr/0任意指针类型
任意非常量的指针void*
任意对象的指针const void*
  1. 类类型的转换
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值