0
点赞
收藏
分享

微信扫一扫

逆波兰表达式

栖桐 2023-07-05 阅读 79

在这里插入图片描述


目录

一、C语言中函数的分类

1.1 库函数


那我们应该如何学习C语言呢?这里给大家一个网站(以往也提到过),可以帮我们深入了解库函数:点击跳转

但是库函数必须知道的一个秘密就是:使用库函数,必须包含#include对应的头文件。

1.2 自定义函数

例如写一个自定义函数来找出两个整数之间的最大值。

#include <stdio.h>

int find_max(int x, int y)
{
	if (x > y)
		return x;
	else
		return y;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int Max = find_max(a, b);
	printf("最大值是:%d", Max);

	return 0;
}

​再举一个例子:写一个自定义函数可以交换两个整型变量的内容。

我想大部分初学者一定会这么写:

#include <stdio.h>

void swap(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	printf("a = %d\nb = %d\n", a, b);

	return 0;
}

但是结果却和我们想的不太一样,当输入10和20,会发现交换后和交换前一模一样

在这里插入图片描述

我们可以通过监视来找程序错误:

在这里插入图片描述

大家注意看axby的地址,a对应xb对应y的地址都不一样怎么可能交换的了值呢?

官方说法:a,b叫做实际参数,x和y叫做形式参数,当函数调用时,实参传递给形参,形参就会有自己的空间(地址),所以形参的修改不会影响实参。

因此,可以用指针来建立联系,代码修改后为:

#include <stdio.h>

void swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a, &b);
	printf("a = %d\nb = %d\n", a, b);

	return 0;
}

【程序结果】

在这里插入图片描述

代码详解:把a的地址交给xb的地址交给y,此时x中存的就是a的地址,y中存的是b的地址,所以*x就是a*y就是b,这里运用到指针中的解引用。对于指针不熟的同学们可以看看这篇博客:点击跳转

1.3 void在自定义函数中的应用

#include <stdio.h>
void tmp(void)
{
	printf("hello world!\n");
}
int main()
{
	tmp();
	return 0;
}

代码详解:假设自定义函数tmp,但是我不给它传任何参数,所以可以在定义函数括号里写上void,表明这个函数在调用时不能传参,现在只希望函数内部打印hello world!,因为不需要任何返回,所以它的返回类型为void

二、简单介绍两个库函数

2.1 实现字符串拷贝(strcpy)

【文档描述】

在这里插入图片描述

【代码样例】

#include <stdio.h>
#include <string.h> //使用strcpy需要包含的头文件
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world!";
	strcpy(arr1, arr2);   //数组名就是指针
	printf("%s\n", arr1); //%s用来打印字符串
	return 0;
}

【程序结果】

在这里插入图片描述

2.2 设置内存(memset)

【文档描述】

在这里插入图片描述

【代码样例】

#include <stdio.h>
#include <string.h> //memset需要包含头文件
int main()
{
	char arr1[20] = "welcome to China";
	memset(arr1, 'x', 7);   //'x'对应的是ASCII码,是整型
	printf("%s\n", arr1);
	return 0;
}

【程序结果】

在这里插入图片描述

三、函数的参数

3.1 实际参数(实参)

3.2 形式参数(形参)

四、函数的调用

4.1 传值调用

在【2.1自定义函数】写了一个自定义函数来找出两个整数之间的最大值,就是传值调用。

4.2 传址调用

那【2.1自定义函数】写了自定义函数可以交换两个整型变量的内容,则就是传址调用。

五、函数的嵌套调用和链式访问

5.1 嵌套调用

什么是嵌套调用呢?来看看下面的代码:

#include <stdio.h>
void new_line()
{
	printf("hello world\n");
}
void three_line()
{
	for (int i = 0; i < 3; i++)
	{
		new_line();
	}
}

int main()
{
	three_line();
	return 0;
}

【程序结果】

在这里插入图片描述

注意,函数可以嵌套调用,但不能嵌套定义!

那什么是嵌套定义呢?举个例子大家就明白了

#include <stdio.h>
int main()
{
	int Add(int x, int y)
	{
		return x + y;
	}

	return 0;
}

main函数也是函数,main函数只能调用Add函数,而不能定义在函数内部。

5.2 链式访问

#include <stdio.h>
#include <string.h>
int main()
{
	printf("%d\n", strlen("abcdef"));

	return 0;
}

5.3 笔试题

以下代码输出的结果是什么?

#include <stdio.h>
#include <string.h>
int main()
{
	printf("%d",printf("%d", printf("%d",43)));

	return 0;
}

【解析】

首先,我们可以在cplusplus这个网站搜索printf来查看它的返回值:

在这里插入图片描述

接下来我们从里向外分析,printf要打印一个整型,而这个整型是来自printf(“%d”, printf(“%d”, 43))的返回值,接下来的printf又要打印printf("%d", 43)的返回值。

所以首先程序会先打印43,打印完43之后就会打印printf("%d", printf("%d", 43))的返回值,而43的字符总数是2,所以会在屏幕中打印2,最后再打印整个printf("%d", printf("%d", printf("%d", 43)))的返回值,也就是1,所以最后会在屏幕上打印4321。

六、函数的声明和定义

6.1 函数的声明

在这里插入图片描述
在这里插入图片描述

有的初学者可能会把函数定义写在main函数后面,但我们要知道编辑器扫描代码是从上往下扫的,当扫到Add(a, b)发现前面没有见过Add函数,所以就会报错。

那如何纠正错误呢,只要在main函数前声明就行了。

在这里插入图片描述

6.2 函数的定义

像上面刚刚写的代码,可以直接把函数定义放到main函数前,是不是更加简洁。

在这里插入图片描述

6.3 声明和定义的拓展

其实我想告诉大家,实际上函数的声明和定义不是这样用的,上面的定义和声明只是语法展示。真正一个工程中,函数的定义和声明又是如何写的呢?我们接着往下看

比方还是求两个数的和

先新建一个头文件Add.h,然后再定义一个源文件Add.c,接下来我把函数的定义放到Add.c中,对于函数的声明,我放到Add.h中。如果想使用Add函数,只需在text.c中加上#include "Add.h"即可。(函数的声明一般都放在头文件中,函数的定义(实现)放在源文件中)。注意#include只包含头文件,库里提供的函数用尖括号,自己写的头文件用双引号。

在这里插入图片描述

  • 拆成三个文件的好处(了解)

最后,为什么一个.c文件就可以写完这些代码,而要把它拆成3个文件呢?其实它是有好处的。

①模块化开发(分工)

假设要写一个计算器程序,A程序员写加法,B程序员写减法,C程序员写乘法,D程序员写除法,如果没有多个文件设计,这些程序员都要在text.c中完成,这根本实现不了。有了多文件的设计,能够有效提高效率。

②有利于代码的隐藏

七、递归

7.1 什么是递归

为了了解递归,首先写一个史上最简单的递归(会发生错误的递归):

在这里插入图片描述

main函数在自己调用自己。但调用着就会发现,程序崩了,它不会一直死递归下去。

同样可以按F10来观察程序

接下来程序就会弹出下面的窗口,stack overflow:栈溢出。

在这里插入图片描述

这就要牵扯到内存中的栈区、堆区和静态了

在这里插入图片描述

每调用函数,都会为本次函数,在内存的栈区上开辟一块内存空间。

接下来回到刚刚的代码,每调用一次main函数就会在栈区开辟一块内存空间,一直开辟总会有一天把栈区给“榨干”了,这时栈就溢出了。

在这里插入图片描述

这里为大家推荐两个问答社区网站:

  1. 这个网站(国外)相当于一个程序员的问答社区:点击跳转
  2. 思否是国内的程序员问答社区:点击跳转

7.2 递归的两个必要条件

7.3 递归练习

  1. 接收一个整型值(无符号),按照顺打印它的每一位。输入:1234,输出:1 2 3 4

【解题思路】

在这里插入图片描述

【代码实现】

#include <stdio.h>
void Print(unsigned int x)
{
	if (x > 9)  //判断两位数
	{
		Print(x/10);
	}
	printf("%d ", x % 10);
}
 
int main()
{
	unsigned int  num = 0;
	scanf("%u", &num);//%u - 输入无符号值
	//写一个函数打印num的每一位
	Print(num);
	return 0;
}

【画图解释递归】

首先大家要知道,递归其实是两个词,递:递推,归:回归。(很重要!!!)

先递推(黑色),后回归(红色)

在这里插入图片描述

  1. 编写函数不允许创建临时变量,求字符串长度。

首先如果能创建临时变量的话,我们应该怎么写呢?

#include <stdio.h>
int ch_len(char* arr)
{
	int count = 0;//计时器
	while (*arr != '\0')
	{
		count++;
		arr++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "hello";
	int ret = ch_len(arr);
	printf("%d\n",ret);
}

【代码详解】

在这里插入图片描述

那不允许创建临时变量该怎么写呢?既然讲到了递归,就得用递归来解决。

【解题思路】

ch_len(“hello”),如果第一个字符不是'\0',是不是就能转化成1+ch_len(“ello”),接下来ch_len(“ello”)的第一个字符又不是'\0',是不是又能转化成1+1+ch_len(“llo”),接下来以此类推直到'\0'

【代码实现】

int ch_len(char* arr)
{
	if (*arr != '\0')
		return 1 + ch_len(arr + 1);
	else
		return 0;//当碰上第一个字符为\0,就返回0;
}
int main()
{
	char arr[] = "hello";
	int ret = ch_len(arr);
	printf("%d\n",ret);
}

【画图解释递归】

先递推(黑色),后回归(红色)

在这里插入图片描述

八、迭代及练习

  1. 求n的阶乘(不考虑溢出)

【解题思路】

首先看看递归实现

在这里插入图片描述

【代码实现】

#include <stdio.h>
int Fac(int n) //形参的名字可以和实参一样
{
	if (n <= 1)
	{
		return 1;
	}
	else
	{
		return n * Fac(n - 1);
	}
}
int main()
{
	int n = 0;
	//输入n的值
	scanf("%d", &n);
	int ret = Fac(n);
	printf("%d\n", ret);
	return 0;
}

【画图分析递归】

递推(黑),回归(红)

在这里插入图片描述

在这里插入图片描述

【非递归法】

#include <stdio.h>
int Fac(int n) 
{
	int i = 0;
	int ret = 1;
	for (i = 1; i <= n; i++)
	{
		ret = ret * i;
	}
	return ret;
}
int main()
{
	int n = 0;
	//输入n的值
	scanf("%d", &n);
	int ret = Fac(n);
	printf("%d\n", ret);
	return 0;
}
  1. 求第n个斐波那契数

【递归解题思路】

在这里插入图片描述

【代码实现】

#include <stdio.h>
int Fib(int n)
{
	if (n <= 2)
	{
		return 1;
	}
	else
	{
		return Fib(n-1) + Fib(n - 2);
	}
}
int main()
{
	int n = 0;
	//输入n的值
	scanf("%d", &n);
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}

【递归图】

在这里插入图片描述

实际上,当输入50时,编辑器的光标还在闪烁,有的人可能会认为程序挂掉了,其实并没有,程序此时此刻还在长时间计算第50个斐波那契数

在这里插入图片描述

所以这题使用递归还存在局限性,那么接下来我们来尝试迭代(循环)

【迭代解题思路】

在这里插入图片描述

【代码实现】

#include <stdio.h>
int Fib(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (n>2) //当n = 1或者2时可以不用算,因为结果都是1,
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n = 0;
	//输入n的值
	scanf("%d", &n);
	int ret = Fib(n);
	printf("%d\n", ret);
	return 0;
}
举报

相关推荐

0 条评论