跟我从零开始学C++(⭐函数专项⭐)超超详细版plus

引言

  芜湖~小伙伴们,终于到了呼声最高的函数专项了,本章小杨还是采用了之前的自带锚点的功能的版本,方便大家随时来看,记得收藏,我相信小伙伴们学完这章肯定会对函数有个新的了解,对函数的认知会更加全面和系统。同理,除了有关解释部分,一定也要对代码里的注释要有所研究,会有你意想不到的惊喜。
  好了废话不多说,小伙伴们开始吧,冲冲冲!!!!

函数及其使用

目录

1.函数的定义
2.小练习
3.参数传递–指针
4.参数传递–引用
5.数组作为参数传递
6.数组作为返回值
7.练习
8.变量的作用范围
9.变量的存储类别
10.内联函数
11.函数的重载
12.参数的默认值
13.函数指针
14.嵌套的调用
15.递归的调用
16.练习题

1.函数的定义和使用

  函数是一段可以重复使用的代码,用于执行特定任务。在C++中,函数通过返回类型、函数名称和参数列表来定义。我们通常会用一个函数来实现一个功能,在程序的每个地方,当我们的程序需要调用该功能的时候,我们就可以利用该函数。

  • 代码示例:
#include <iostream>

using namespace std;

/*
	函数的定义
		函数的定义由函数类型、函数名和参数组成,称为函数头。
		函数名是标识符,必须遵守标识符命名规则。
		由两个大括号括起来的是函数体。
	函数类型(返回值)
		函数类型就是函数返回值的类型。
		函数可以没有返回值,此时函数类型为void。
		函数必须指定返类型。
	函数参数
		形参(形式参数),定义函数时括号中的参数
		实参(实际参数),调用函数时括号中的参数。


*/
int mySum(int n) //	n是形参
{
	int sum = 0;
	for (int i = 1; i <= n; i++)
	{
		sum += i;
	}
	return sum;
}


//	没有返回值的函数,类型为void。
void myFunc()
{
	cout << "myFunc()" << endl;
	return;
}
//	main称为主调函数,mySum称为被调函数。
int main()
{
	cout << mySum(10) << endl; // 10是实参
	cout << mySum(9) << endl;
	return 0;
}


回到目录

2.小练习

  小伙伴们,让我们先来对这个函数的定义和使用来个小练习,保证自己对函数的定义和使用都有了清晰的认知。
1.求两个数中的较大值
2.求m的n次方

  • 代码示例练习1:
#include <iostream>

using namespace std;

//	函数可以先声明再定义
int myMax(int x, int y); //	声明函数

int main()
{
	cout << myMax(1, 100) << endl;
	return 0;
}

int myMax(int x, int y)
{
	if (x > y)
	{
		return x;
	}
	return y;
}
  • 代码示例练习2:
#include <iostream>

using namespace std;

//	编写函数计算m的n次方
float myPower(float m, int n);

int main()
{
	int m = 2;
	int n = 3;
	cout << myPower(m, n) << endl;
	cout << "main m: " << m << endl;
	return 0;
}

float myPower(float m, int n)
{
	float a = 1.0;
	for (int i = 0; i < n; i++)
	{
		a *= m;
	}
	m++;
	cout << "myPower m: " << m << endl;
	return a;
}

回到目录

3.参数传递–指针

  在C++中,参数可以通过指针和引用传递。通过指针和引用传递参数可以允许函数修改原始数据,因为它们传递的是数据的地址或引用,而不是数据的副本。我们是不可以用数值来传递的,我们从上边函数的定义就可以知道,我们用数值传递时候,我们改变的是形参,而我们要实现功能,是改变的实参,所以我们用指针和引用时我们可以直接改变实参。

  • 代码示例:
#include <iostream>

using namespace std;

//	定义函数交换m,n的值。
void my_swap(int* p1, int* p2);

int main()
{
	int m = 10;
	int n = 20;
	int* p1 = &m, * p2 = &n;
	cout << "main: " << "p1: " << p1 << " p2: " << p2 << endl;
	my_swap(p1, p2);
	cout << "main: " << "m: " << m << " n: " << n << endl;
	cout << "main: " << "&m: " << &m << " &n: " << &n << endl;
	cout << "main: " << "p1: " << p1 << " p2: " << p2 << endl;
	cout << "main: " << "&p1: " << &p1 << " &p2: " << &p2 << endl;
	return 0;
}

void my_swap(int* p1, int* p2)
{
	int tmp = *p1; // 通过解引用取得main中m的值
	*p1 = *p2; //	通过p1解引用取得main中m,并修改m的值
	*p2 = tmp; //	通过p2解引用取得main中n,并修改n的值
	cout << "my_swap: " << "p1: " << p1 << " p2: " << p2 << endl;
	cout << "my_swap: " << "&p1: " << &p1 << " &p2: " << &p2 << endl;
	//	被调函数中修改形参的指针指向,不应影响主调函数中实参的指针指向。
	p1 = &tmp;
	p2 = &tmp;
}

回到目录

4.参数传递–引用

解释说明同指针,但和指针有所不同

  • 指针和引用的不同:
  • 指针:
    可以为空 (nullptr)。
    可以指向不同的对象(通过重新赋值)。
    必须使用 * 运算符来解引用。
    可以通过指针来修改指向的值。
    可以通过指针来获取变量的地址。
  • 引用:
    不能为空,必须初始化。
    一旦初始化,就不能更改引用的对象。
    使用时不需要特殊的解引用语法。
    直接操作引用就是操作原始对象。
    通常用于函数参数,以允许函数修改原始数据。
  • 代码示例:
#include <iostream>

using namespace std;

//	定义函数交换m,n的值。
void my_swap(int& a, int& b);

int main()
{
	int m = 10;
	int n = 20;
	my_swap(m, n);
	cout << "m: " << m << " n: " << n << endl;

	return 0;
}

void my_swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

回到目录

5.数组作为参数传递

  数组作为参数传递时,实际上传递的是指向数组首元素的指针。我们经常会在需要处理数组数据的函数中,如排序、搜索或计算数组中的统计信息中调用数组作为参数传递。

  • 代码示例:
#include <iostream>

using namespace std;

//	数组作为参数传递,传递的是数组的首地址
void my_reverse(int a[], int len);

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	my_reverse(a, 5);
	for (int i = 0; i < 5; i++) cout << a[i] << ", ";
	cout << endl;
	return 0;
}

void my_reverse(int a[], int len)
{
	int m = len / 2;
	for (int i = 0; i < m; i++)
	{
		int tmp = a[i];
		a[i] = a[len - i - 1];
		a[len - i - 1] = tmp;
	}
}

回到目录

6.数组作为返回值

  有时候我们需要返回多个值的函数中,尤其是当这些值形成一个序列或集合时,我们就可以选用数组作为返回值,虽然C++不允许直接返回数组,但可以通过返回指向数组的指针来模拟这一行为。

  • 代码示例:
#include <iostream>

using namespace std;

/*
	C++中函数不能直接返回一个数组,但是可以通过返回指针来实现返回一个数组。
	C++中很少使用在被调函数中生成数组并返回给主调函数的方式,
	如果一定要使用,使用动态内存分配方式生成数组并返回。
*/
int* func();

int main()
{
	int* p = func();
	for (int i = 0; i < 5; i++)
	{
		cout << p[i] << ", ";
	}
	cout << endl;

	delete[]p;
}

int* func()
{
	int* a = new int[5];
	for (int i = 0; i < 5; i++)
	{
		a[i] =  i + 1;
	}
	return a;
}

回到目录

7.练习题

  了解了数组可以作为参数传递,也可以作为返回值,我们不妨来做个小练习,// 编写拼接字符串函数, p1为目标字符串,p2为源字符串。
  提示:p1,p2作为参数,p1作为返回值

  • 代码示例:
#include <iostream>

using namespace std;

//	编写拼接字符串函数, p1为目标字符串,p2为源字符串
void str_concat(char* p1, char* p2);

int main()
{
	char c1[20] = "abcd";
	char c2[20] = "efg";

	str_concat(c1, c2);
	cout << "c1: " << c1 << endl;
	return 0;
}

void str_concat(char* p1, char* p2)
{
	//	计算p1的长度,并把p1移动到字符串末尾
	p1 += strlen(p1);
	cout << "*p1: " << (int)*p1 << endl;

	//	从p1末尾开始拼接p2.
	while (*p2 != '\0')
	{
		*p1 = *p2;
		p1++;
		p2++;
	}
	//	拼接完成后设置字符串结束标志
	*p1 = '\0';
	
}

回到目录

8.变量的作用范围

  在C++中,变量的作用范围(Scope)是指程序中变量可以被访问和使用的区域。一个变量的作用范围由其声明位置决定,在函数这一章我们把变量分为两种来看,后续还有其他我们讲到的时候会说,这里我们先说到全局变量和局部变量。

  • 代码示例:
#include <iostream>

using namespace std;

//	全局变量
int i = 100;

void func();

int main()
{
	i++;
	func();
	cout << "main: i: " << i << endl;
	return 0;
}

void func()
{	
	//	使用全局变量i
	cout << "func: i: " << i << endl;
	i++;
	cout << "func: i: " << i << endl;
	//	局部变量
	int i = 0;
	//	使用局部变量i
	cout << "func: i: " << i << endl;
	i++;
	cout << "func: i: " << i << endl;
	//	通过::访问全局变量
	cout << "func: ::i: " << ::i << endl;
}

回到目录

9.变量的存储类别

  在C++中,变量的存储类别(Storage Class)决定了变量的生命周期、作用域和初始化方式。C++提供了几种不同的存储类别(还有其他几种不常见的不做陈列)主要说明这个静态变量。

  • 自动存储期限(Auto Storage Class):
    默认存储类别,无需显式声明。
    作用域:块作用域(如函数内部或复合语句内部)。
    生命周期:从声明点到作用域结束。
    初始化:未初始化。

  • 静态存储期限(Static Storage Class):
    使用 static 关键字声明。
    作用域:块作用域或全局作用域。
    生命周期:程序执行期间一直存在。
    初始化:默认初始化为0(全局静态变量)或未初始化(局部静态变量)。

  • 注册存储期限(Register Storage Class):
    使用 register 关键字请求将变量存储在CPU寄存器中,以加快访问速度。
    作用域:块作用域。
    生命周期:从声明点到作用域结束。
    初始化:未初始化。

  存储类别的选择对程序的性能和资源管理有重要影响。正确使用存储类别可以帮助优化程序内存使用和执行效率。

  • 代码示例:
#include <iostream>

using namespace std;

int func(int n);

int main()
{
	for (int i = 1; i < 5; i++)
	{
		cout << func(i) << endl;
	}
	
	return 0;
}

int func(int n)
{
	static int m = 1;
	int i = n;
	m = m * i;
	
	return m;
}

回到目录

10.内联函数

  内联函数(Inline Function)是C++中的一种函数优化机制。当你在函数声明前加上 inline 关键字时,你是在告诉编译器尽可能地用函数体中的代码替换每一次函数调用,从而减少函数调用的开销。这种替换过程称为函数展开。

内联函数通常用于以下情况:

  • 函数体较小:内联函数的代码行数较少,这样展开后的代码不会使程序体积过大。
  • 频繁调用:如果某个函数被频繁调用,那么将其内联可以减少调用栈的深度,提高性能。
  • 避免性能损失:对于那些对性能要求极高的代码,内联可以减少函数调用的开销。

内联函数的声明和定义通常放在头文件中,因为它们可能会被多个源文件包含。

  • 代码示例:
#include <iostream>

using namespace std;

inline int add(int a, int b);

int main()
{
	int a = 10;

	for (int i = 0; i < 50; i++)
	{
		//	cout << a +i  << endl;
		cout << add(a, i) << endl;
	}

	return 0;
}

int add(int a, int b)
{
	return a + b;
}

回到目录

11.函数的重载

  函数重载(Function Overloading)是C++中的一种特性,允许在同一作用域内存在多个同名函数,只要它们的参数列表不同即可。函数的重载可以根据参数的数量、类型或者顺序来区分。

  当调用重载函数时,编译器会根据传递给函数的参数来决定调用哪个函数版本,这个过程称为函数重载解析

函数重载的条件:

  • 相同的函数名:重载的函数必须具有相同的名字。
  • 不同的参数列表:参数列表的不同可以体现在参数的数量、类型或者顺序上。
  • 在同一作用域内:重载的函数必须在同一作用域内,例如同一个类或者同一个全局作用域。
  • 代码示例:
#include <iostream>

using namespace std;

int myAdd(int a, int b);

float myAdd(int a, float b);

int main()
{
	int a1 = 10, a2 = 20;
	float b1 = 11.1, b2 = 22.2;

	cout << myAdd(a1, a2) << endl;
	cout << myAdd(a1, b1) << endl;

	return 0;
}

int myAdd(int a, int b)
{
	cout << "myAdd(int a, int b)" << endl;
	return a + b;
}

float myAdd(int a, float b)
{
	cout << "myAdd(int a, float b)" << endl;
	return a + b;
}

回到目录

12.参数的默认值

  在C++中,函数参数可以具有默认值。如果在调用函数时没有为这些参数提供值,将自动使用默认值。这允许函数更加灵活,因为它们可以适应多种不同的调用情况。

  • 代码示例:
#include <iostream>

using namespace std;

/*
	声明或定义函数时,可以预先为函数指定默认参数值。
	默认值通常在函数声明处给出。
	默认值必须从右向左顺序定义,当形参有默认值时,其后所有的形参都要有默认值。
*/
int myMax(int a, int b , int c );

int main()
{
	int a = 1, b = 100, c = 1;
	cout << myMax(a, b, c) << endl;
	cout << myMax(a, b) << endl;
	cout << myMax(a) << endl;
	return 0;
}


int myMax(int a, int b , int c )
{
	int max = a;
	if (max < b)
	{
		max = b;
	}
	if (max < c)
	{
		max = c;
	}
	return max;
}

回到目录

13.函数指针

  函数指针是一个指向函数的指针,它存储了函数的地址。在C++中,函数指针可以用来调用函数、传递函数作为参数或者将函数作为返回值。函数指针对于实现回调函数、事件处理和某些设计模式(如策略模式)非常有用。

  • 代码示例:
#include <iostream>

using namespace std;

int myAdd(int a, int b);

int main()
{
	//	定义函数指针,用于指向具有两个int层参数,并且返回值为int型参数的函数。
	int (*fp)(int, int);
	
	//	函数名就是函数的首地址
	cout << "myAdd: " << myAdd << endl;

	//	函数指针fp指向myAdd函数
	fp = myAdd;
	
	//	使用函数指针调用函数
	int a = fp(10, 20);
	cout << "a; " << a << endl;

	return 0;
}

int myAdd(int a, int b)
{
	return a + b;
}

回到目录

14.嵌套的调用

  嵌套的调用指的是在一个函数的执行过程中调用另一个函数。这种调用可以是直接的,也可以是嵌套多层的。嵌套调用通常用于将复杂的问题分解为更小的子问题,然后分别解决。我们经常会在遇到一个问题时,碰到另一个问题,我们可以把这个问题作为新的问题去解决,放在另一个函数里。

  • 代码示例:
#include <iostream>

using namespace std;

//	计算n个阶乘结果的求和。 1! + 2! + 3! ... + n!
int f1(int n);

//	计算m的阶乘
int f2(int m);

int main()
{
	cout << f1(5) << endl;
	return 0;
}

int f1(int n)
{
	int sum = 0;
	for (int i = 1; i <= n; i++)
	{
		sum += f2(i);
	}	
	return sum;
}

int f2(int m)
{
	int n = 1;
	for (int i = 1; i <= m; i++)
	{
		n *= i;
	}
	return n;
}

回到目录

15.递归的调用

  递归的调用是指函数直接或间接地调用自身。递归是一种强大的编程技术,它可以将复杂的问题简化为多个相似的子问题。递归函数通常有一个或多个基案(基本情况),当满足这些基案时,递归调用会停止。递归和嵌套类似不过是在调用自己的函数。

  • 代码示例:
#include <iostream>

using namespace std;

/*
	n!,当n = 0 或 1时,n! = 1,当n > 1时, n = n*(n-1)!
*/

long long power(long long n)
{
	long long r;
	if (n > 1)
	{
		r = n* power(n - 1);
	}
	else
	{
		r = 1;
	}
	return r;
}



int main()
{
	cout << power(4) << endl;	
}

回到目录

16.练习题

  学完了有关函数的所有基础知识怎么能不来两道练习题呢。
1.递归和非递归的方式来计算斐波那契数列的结果
2.将一个数字字符串转换为对应的整数
3.编写函数,删除字符串中第一个指定的字符。
4.计算最后一个单词的长度

  • 练习代码展示1:
#include <iostream>

using namespace std;

//	计算斐波那契数列的结果
int fib(int n);

//	非递归方式计算斐波那契数列的结果
int fib1(int n);


int main()
{
	for (int i = 0; i < 50; i++)
	{
		cout << fib1(i) << ", ";
	}
	cout << endl;

	return 0;
}

int fib(int n)
{
	if (n == 0)
	{
		return 0;
	}
	else if (n == 1)
	{
		return 1;
	}
	else
	{
		return fib(n - 1) + fib(n - 2);
	}
}


int fib1(int n)
{
	if (n == 0)
	{
		return 0;
	}
	else if (n == 1)
	{
		return 1;
	}
	else
	{
		int a1 = 0, a2 = 1, tmp;
		for (int i = 2; i <= n; i++)
		{
			tmp = a2;
			a2 = a1 + a2;
			a1 = tmp;
		}
		return a2;
	}
}
  • 练习代码展示2:
#include <iostream>

using namespace std;

//	将一个数字字符串转换为对应的整数
int myConvert(const char* p);

int main()
{	
	int i = myConvert("12345");
	cout << "i: " << i << endl;
	return 0;
}

int myConvert(const char* p)
{
	int n = 0;
	for (int i = 0; p[i] != '\0'; i++)
	{
		n = n * 10 + (p[i] - '0');
	}
	return n;
}
  • 练习代码展示3:
#include <iostream>

using namespace std;

//	编写函数,删除字符串中第一个指定的字符。

void delChar(char* p, char c);

int main()
{
	char c[20] = "abcd ef";
	delChar(c, 'e');
	cout << c << endl;
	return 0;
}

void delChar(char* p, char c)
{
	for (int i = 0; p[i] != '\0'; i++)
	{
		if (p[i] == c)
		{
			for (int j = i; p[j] != '\0'; j++)
			{
				p[j] = p[j + 1];
			}
			break;
		}
	}
}
  • 练习代码展示4:
#include <iostream>

using namespace std;

bool isAlpha(char c)
{
	if (c >= 'a' && c <= 'z') return true;
	if (c >= 'A' && c <= 'Z') return true;
	return false;
}

int main()
{
	/*
		计算最后一个单词的长度。
		最后一个字母,到最后一个空格后的第一个字母,之间的算一个单词
		"abc 'a'! "
	*/
	char c[] = "abc 'abcde'! ";
	int count = 0;
	int len = strlen(c);
	for (int i = len - 1; i >= 0; i--)
	{
		if (isAlpha(c[i]))
		{			
			count++;
			if (!isAlpha(c[i - 1]))
			{
				break;
			}
		}  
	}
	
	cout << "count: " << count << endl;

	return 0;
}

回到目录

结语

小伙伴们,经过指针,数组还有函数专项,我相信大家对C++的认识已经不是一个小新手了,有关C++的基础知识还有最后一点我们就可以讲解,有关C++和面向对象了,大家一定要加油呀。我相信到了这一步大家对我的肯定和支持我相信都没有辜负,大家接下来也要持续关注哦哟,第一次看到这里的小伙伴们也一定要点个收藏加关注,小杨会持续为大家写出更优质更好的内容。争取帮助到大家在C++的学习路上少走一些弯路。

  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值