【C语言】深入学习函数(万字)

news2024/11/26 18:50:03

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在回炉重造C语言(2023暑假)
✈️专栏:【C语言航路】
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 一、C语言中函数的分类
      • 1.1 库函数
      • 1.2 自定义函数
      • 1.3 void在自定义函数中的应用
  • 二、简单介绍两个库函数
      • 2.1 实现字符串拷贝(strcpy)
      • 2.2 设置内存(memset)
  • 三、函数的参数
      • 3.1 实际参数(实参)
      • 3.2 形式参数(形参)
  • 四、函数的调用
      • 4.1 传值调用
      • 4.2 传址调用
  • 五、函数的嵌套调用和链式访问
      • 5.1 嵌套调用
      • 5.2 链式访问
      • 5.3 笔试题
  • 六、函数的声明和定义
      • 6.1 函数的声明
      • 6.2 函数的定义
      • 6.3 声明和定义的拓展
  • 七、递归
      • 7.1 什么是递归
      • 7.2 递归的两个必要条件
      • 7.3 递归练习
  • 八、迭代及练习

一、C语言中函数的分类

在C语言中,函数分为库函数自定义函数

1.1 库函数

  • 我们知道在学习C语言编程的时候,总是在一个代码写完之后就迫不及待想知道结果,想把结果打印到屏幕上,这时候我们就会频繁的使用printf函数。(printf
  • 在编程的过程中我们会频繁的做一些字符串的拷贝工作。(strcpy
  • 在编程中,我们有时也会计算nk次方这样的运算。(pow

以上的代码在开发的过程中每个程序员都可能用到的,为了支持可移植性和提高程序效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。


那我们应该如何学习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)

【文档描述】

在这里插入图片描述

功能:把ptr指向空间的前``num的数据设置成value`这个值

【代码样例】

#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;
}

strlen的返回值做了printf函数的参数,这就是链式访问。

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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/722422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

文件上传+文件包含组合式getshell

实验目的 通过本实验&#xff0c;了解文件包含的特点&#xff0c;掌握图片马的执行方法&#xff0c;文件上传文件包含组合式getshell。 实验环境 操作机&#xff1a;kali 靶机&#xff1a;Windows 2007 用户名/密码&#xff1a;college/360College 实验地址&#xff1a;http…

jenkins部署springboot项目

jenkins部署springboot项目 1、创建一个项目 上传到gitee 1、创建项目 2、上传到git 2、jenkins创建一个pipeline项目 Pipeline简介 1&#xff09;概念 Pipeline&#xff0c;简单来说&#xff0c;一套运行在 Jenkins 上的工作流框架&#xff0c;将原来独立运行于单个或者…

优化chatGPT提示词的Prompts

你扮演一个专业的chatGPT提示词工程师&#xff0c;我将为您提供我的提示词&#xff0c;它用三个反引号分隔&#xff0c;请根据openai发布的提示词标准和优化技巧&#xff0c;改进和优化我的提示词&#xff0c;让chatGPT能够更好的理解。 我的第一个提示词是&#xff1a;“”“……

【UI设计】新拟态风格

新拟态风格 1.有且只有一个光源照射 那作者在追波上按钮也好还是卡片处理也好&#xff0c;仔细观察不难发现&#xff0c;它定了一个光源&#xff0c;是从左上向右下照射的&#xff0c;所以&#xff0c;越靠近光源的部分&#xff0c;越亮&#xff0c;远离光源的越暗&#xff08;…

卡尔曼滤波实例分析(一)

1 现实问题 假设一个物体位于1000米处以自由落体运动&#xff0c;地面有一台具有特殊功能的雷达&#xff0c;对其进行观察&#xff0c;现需要对其下落的高度进行测量&#xff1b; &#xff08;1&#xff09;建模 速度&#xff1a;V gt 位置&#xff1a;Y -Vt Y0 &#xff0…

react笔记_06类组件

目录 复习展开运算符 组件什么叫做组件&#xff1f;分类类组件es6新增构造函数语法类组件渲染类组件的this指向类组件的三大属性state作用语法-初始化数据语法-修改state数据语法-获取state中的数据案例 propspropTypes属性(prop-types库)defaultProps属性 refs[1] 字符串形式的…

基于UFLD-v2的改进:UFLD-v2-plus

1 待解决问题 UFLD-v2是一个非常优秀的车道线检测模型。也有一些问题。 1 参数量太大&#xff0c;一个模型600M。 2 不能区分车道线。 2 工作内容 改进方法如下 2.1 降低参数量 对模型网络逐层参数量&#xff0c;可以发现&#xff0c;86%&#xff08;很久之前分析的&…

基于STM32+华为云IOT设计的智能浇花系统

一、前言 随着社会的不断发展和人们生活水平的逐渐提高,人们逐渐追求高质量的生活,很多人都会选择在家里或办公室种植一些花卉以净化家庭空气,陶冶情操,但是很多人忙于工作、学习、出差、旅游或者一些其他的原因,不能及时地对花卉进行照料,短时间内导致很多花卉因缺水分…

100天精通Golang(基础入门篇)——第13天:深入解析Go语言中的字符串(string)及常用函数应用

&#x1f337; 博主 libin9iOak带您 Go to Golang Language.✨ &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &#x1f30a; 《I…

怎么用爱问转换工具在线拆分PDF文件

我们平时在工作中会用到很多PDF文件&#xff0c;有的PDF文件内容很多&#xff0c;里面也有许多分类类别&#xff0c;其实对于这种内容多的PDF文件&#xff0c;可以使用拆分的方式&#xff0c;来获取自己需要的材料&#xff0c;那么&#xff0c;如何快速拆分PDF文件呢&#xff1…

VSCode离线安装Remote-SSH插件

最近想要在VSCode上安装Remote-SSH插件&#xff0c;以便可以远程登录云服务器进行开发或者文件编辑、文件传输等功能&#xff1b;但是VSCode中在线安装一直失败&#xff0c;所以记录一下离线安装的方法&#xff1b; 文章目录 一、下载VSCode Remote-SSH的离线插件&#xff0c;即…

python: PyCharm 2023.1打包项目成执行程序

IDE 最底部&#xff1a; pyinstaller -i heart.ico -D main.py 生成成功

Jenkins基础介绍以及docker安装Jenkins

Jenkins基础介绍以及docker安装Jenkins 什么是Jenkins&#xff1f; Jenkins是一个可扩展的持续集成引擎 持续集成就是通常说的CI&#xff08;Continues Integration&#xff09; 每次集成都通过自动化的构建&#xff08;包括编译&#xff0c;发布&#xff0c;自动化测试&am…

同一局域网内IP 192.168.1.10 和 IP 10.10.10.8 可以互相访问吗?

同一局域网内IP 192.168.1.10 和 IP 10.10.10.8 可以互相访问吗&#xff1f; 1、网上邻居的方式&#xff1a; 鼠标点击 我的电脑 属性 计算机名&#xff0c;查看一下 计算机名&#xff08;这个可以点击更改&#xff0c;自己设定和更改&#xff09; 查看一下工作组&#xff0c;一…

基于node.js中的serialport模块实现无线传感网上位机功能

半个月前的无线传感网课设上位机的实现遇到了很多困难&#xff0c;特写此文章给有需要的朋友一些帮助&#xff0c;欢迎私信探讨 文章目录 前言一、node.js中的serialport模块二、express框架三、echarts实现拓扑图四、实现下行数据五、成果展示总结 前言 本文所要实现的功能以…

【React】next+antd报错:Module not found: Can‘t resolve ‘antd/es/content‘

Antd Next手册&#xff1a;https://ant.design/docs/react/use-with-next-cn 报错场景 根据官方手册进行项目创建 yarn create next-app antd-demoyarn add antd 得到以下环境&#xff1a; EnvironmentInfoantd5.6.4next13.4.8react18.2.0 安装完依赖后&#xff0c;运行y…

java方法详解

1. 方法概述 1.1 什么是方法 方法&#xff08;method&#xff09;是将具有独立功能的代码块组织成为一个整体&#xff0c;使其具有特殊功能的代码集 注意&#xff1a; 方法必须先创建才可以使用&#xff0c;该过程称为方法定义方法创建后并不是直接运行的&#xff0c;需要手动…

日本 NFT 项目概览与特点总结

日本的 NFT 市场 日本的 NFT 市场起源于与国内动漫和娱乐偶像的合作&#xff0c;重点关注本土文化&#xff0c;文化成为日本 NFT 项目的重要基石。 关键要点&#xff1a; 日本的 NFT 产业具有三个特点&#xff1a;广泛的知识产权&#xff08;IP&#xff09;、低 FUD 水平以及…

Ka/Ks介绍和分析

什么是Ka/Ks? 在遗传学中&#xff0c;Ka/Ks表示的是两个蛋白编码基因的非同义替换率&#xff08;Ka&#xff09;和同义替换率&#xff08;Ks&#xff09;之间的比例。这个比例可以判断是否有选择压力作用于这个蛋白质编码基因。 如果你手头有两个不同物种的同一个基因的序列…

JavaWeb项目-超市订单管理系统【Day02】

密码修改 1、编写接口方法和mybatis的SQL映射文件 Mybatis配置多参数SQL语句 当我们的SQL语句中有多个参数的时候&#xff0c;需要设置每个参数名对应的接口参数&#xff0c;不然会报错&#xff1a; Parameter ‘id’ not found. Available parameters are [argl, argg, par…