零基础理解函数(更新了)


前言

对于任何的编程语言来说,函数都是其绕不开的一大重点。我们来了解一下函数相关知识(会持续更新函数系列)

初识函数第一弹

一、函数是什么?

函数是一段独立的代码,我们一般会封装一个函数用来完成特定的任务,在某些需要她的场景使用,多数情况下函数是独立于程序存在的,对于C语言来说,函数可以是独立于main函数的。

为什么会出现函数这个概念呢?在过去几年的学习中,我们所了解的函数并不相同,编程中出现的函数是用来封装功能的,前大佬们将某个功能的代码块封装成一个部分,那么我们就可以对其进行调用,以免我们书写同样功能的代码,这大大节约了我们的时间。另一个点,如果我们在开发将所有代码全都放在main函数中,不难想象main函数会是怎样庞大,但是使用函数就可以避免这一点,将整个项目模块化,便于维护,也增加可读性。

二、函数

1.函数的组成

函数名,参数,函数体,返回类型,这些是组成函数必不可少的部分。
定义一个函数,其实可以类比我们定义一个变量的方式
变量的定义 :数据类型 变量名
函数也是如此,只是多了参数。首先确定函数是否有返回值,有返回值就需要按照返回值来定义其数据类型,然后后面紧跟函数名,然后跟(参数)
函数体就是双括号里面的内容
返回值是return后面的内容
在这里插入图片描述

在这个地方可能会对void所定义的函数产生疑问,首先标准C语言允许其写return,但没有返回值。也就是说我们的函数可以定义成

void add(int a,int b){
函数内容
}//像这种

这时候我们函数体中就不要返回值了,也就是说不需要写return 0;但是我们可以写return;没有返回值的return
也就是说写成:

void add(int a,int b){
函数内容
return;
}

其实一般我们使用void这种形式定义函数,就是为了让它做一些不需要返回值的事情,比如自己封装一个打印函数,或者改变一块内存中的数据等等

2.调用函数

接下来我们讨论函数调用问题,还是以前那个Add函数

记住我们调用函数的模式是得紧紧看着我们定义它的形式。(这里讨论的是有返回值的形式
如果使用int定义了一个函数,那么像下图这样使用一个c接受这个函数值的时候,就不能将c定义成别的形式,也得是int。
在这里插入图片描述

即使我们这么定义了,还是有上面这种报错,这是怎么回事。我们知道程序自上到下进行,进入main函数执行到Add函数的时候,程序中还不知道有Add呢,这样就会报找不到标识符的问题。所以我们要进行函数声明,如下:(声明也可以写在main之上的。)
分析:函数从main开始读取,读到声明的时候,它就知道了我们定义过这个Add函数,接下来读到Add(10,20)的时候,就会自动去寻找这个函数,调用它。
在这里插入图片描述
另外一种方式就是直接将函数定义写在main函数之上就不用写函数声明了,一样可以运行
在这里插入图片描述

函数总结第二弹

库函数

每个程序员在开发中可能用到的某种业务代码,为了支持C语言地可移植性和提高效率,所以c语言基础库中提供了一系列类似地库函数,方便程序员调用

基础地库函数有

  1. IO函数 ----------------->输入输出函数
  2. 字符串操作函数
  3. 字符操作函数
  4. 内存操作函数
  5. 时间/日期操作函数
  6. 数学函数
  7. 其他库函数

学习一些基础函数:(学会去读文档是十分重要的http://www.cplusplus.com/reference/)

  • strlen函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WA3ndsLj-1636290419719)(C:/Users/zzzy0/AppData/Roaming/Typora/typora-user-images/image-20211103135124806.png)]

    首先我们看返回值,是size_t类型(表示一个无符号整型),作用是得到一个字符串地长度。参数是传递一个字符指针,指针传递的是地址,也就是可以使用一个字数组传进去嘛

    #include<string.h>
    int main()
    {
    	char arr[] = "abc";
    	size_t len = strlen(arr);
    	printf("%u\n", len);
    
    }
    
  • strcpy函数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7PZa9RY-1636290419743)(C:/Users/zzzy0/AppData/Roaming/Typora/typora-user-images/image-20211103135349612.png)]

    拷贝一个c的字符串从源到目的地

    #include<string.h>
    int main()
    {
    	char arr1[20] = {0};//destination
    	char arr2[20] = "HELLO";//source
    	strcpy(arr1, arr2);
    	printf("%s\n", arr1);
    	return 0;
    }
    
  • memset

    设置内存块函数

    设置从ptr所指向位置开始后面的元素,设置成为value所代表的值,num是设置改变的个数

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8U2Ru1Ow-1636290419745)(C:/Users/zzzy0/AppData/Roaming/Typora/typora-user-images/image-20211103140014158.png)]

    #include<string.h>
    int main()
    {
    	char arr[] = "hello world";
    	memset(arr, 'q', 2);//传递的是字符,但是应该是int,是因为ASCII的
    	//传递了数组的首元素地址,value表示想要替换成为什么元素,这里表示想要换成q,并且替换2个
    	printf("%s", arr);
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fuEnVSv-1636290419747)(C:/Users/zzzy0/AppData/Roaming/Typora/typora-user-images/image-20211103140639756.png)]

自定义函数

库函数只是说满足了大多数程序员共有的业务逻辑,但是还有一些代码功能是需要我们自行封装的,这些就是自定义函数

函数需要先定义后调用,这是逻辑上的顺序

写一个交换数值的代码

void Swap(int x, int y)
{
	int z = 0;
	z = x;
	x = y;
	y = z;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf("交换前:a=%d b=%d\n", a, b);
	Swap(a, b);
	printf("交换前:a=%d b=%d\n", a, b);
}

代码并没有改变两个数的值,我们要注意,函数中x,y和a,b甚至不是同一个地址,怎么会影响ab的值呢

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sur5iXxM-1636290419748)(C:/Users/zzzy0/AppData/Roaming/Typora/typora-user-images/image-20211105021416056.png)]

传递给swap为实参,函数接受的是形参,当函数调用的时候实参传递给形参,形参其实是实参的一份临时拷贝,所以对于形参的修改不会影响实参

那么怎么改动呢?我们要让函数内部链接到main函数中的ab,而指针就可以完美做到

void Swap(int *x, int *y)
{
	int z = 0;
	z = *x;
	*x = *y;
	*y = z;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf("交换前:a=%d b=%d\n", a, b);
	Swap(&a, &b);
	printf("交换前:a=%d b=%d\n", a, b);
}

当我们需要改变主函数中的数时,传址调用

  • 一些注意点

    1. 在进行函数调用的时候,实参必须是确定的值,以便于传递给形参

    2. 如果我们不调用函数,他内部的形参是不会有内存空间的,只有调用才真实分配空间,并且调用后自动销毁了

    3. 形式参数是存储在栈区的

      栈区:局部变量,(形式参数也是局部的阿)

      堆区:动态内存分配(malloc free realloc calloc)

      静态区:全局变量,静态变量

函数的调用

函数是可以嵌套调用的,但是不能嵌套定义------->我们可以在函数中调用其他的函数,但是不能在一个函数中定义一个函数

函数的链式访问

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));//打印4321
	return 0;
}

printf的返回值介绍:返回打印字符个数或者打印错误返回负数

内层打印43,这是两个数字组成的所以第二层打印2,最外层因为这个2所以打印1

函数声明和定义

先去思考怎么使用想要实现的函数然后定义它

一般工程中我们将代码模块化会将一个功能放置在.h跟.c函数中,.h中放置函数声明,.c放置实现,然后再其他.c函数中引用头文件.h,就能使用相应功能

如果希望不让别人看到自己的代码,但是又想让别人使用自己的函数可以考虑给别人lib和.h文件就可以

函数递归

程序调用自身的编程技巧叫做递归 函数自己调用自己就叫递归。

递归非常重要的一点就是找到递归条件,每一次都有限制条件的。其次每一次递归之后都要接近这个限制条件(不然那不就又死递归嘛)

函数递归是最容易造成栈溢出(stackoverflow)问题的,每一次函数调用都会在内存的栈区申请一块空间,但是注意递归的如果没有进行到最后一层是会不断调用的,我们设想一个函数没有限制条件,那么就会无限开辟空间且由于外层的每一层函数都没有结束,空间不会回收,栈迟早得炸

下面我们利用递归实现一个正序打印数字的每一位
我们作图分析这个递归的过程。

在这里插入图片描述
下面是代码实现

void Print(int n)
{
	if (n > 9)
	{
		Print(n / 10);
	}
	printf("%d", n % 10);
}
int main()
{
	int n = 0;
	scanf("%d ", &n);
	Print(n);
	return 0;
}

在这里插入图片描述

最后

以上就是我对于函数的一些学习,希望可以帮助到大家!

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值