C语言函数深度解析

点个赞,互相关注,一起学习!!!

目录

前言

 一、函数是什么?

二、函数的分类

2.1 库函数

2.2  自定义函数

3、函数的参数 

3.1 实际参数(实参):

3.2 形式参数(形参):

3.3 用内存解释形参,实参

4、函数的调用 

4.1 传值调用

4.2 传值调用

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

5.1 嵌套调用

 5.2 链式访问

 6.函数的声明与定义

总结


一、函数是什么

数学中我们常见到函数的概念。但是你了解 C 语言中的函数吗?
维基百科中对函数的定义: 子程序

计算机科学中,子程序英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部份代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。函数在面向过程的语言中已经出现。是结构(Struct)和(Class)的前身。本身就是对具有相关性语句的归类和对某过程的抽象。

二、函数的分类

1.库函数

2.自定义函数

2.1 库函数

为什么有库函数呢:

刚开始的时候,C语言是没有库函数的,C语言的库函数并不是C语言本身的一部分,它是由 编译程序根据一般用户的需要,编制并提供用户使用的一组程序。C的库函数极大地方便了用户,同时也补充了C语言本身的不足。在编写C语言程序时,使用库函数,既可以提高程序的运行效率,又可以提高编程的质量。
简单的总结,C语言常用的库函数都有: www.cplusplus.com
🚀IO函数
🚀字符串操作函数
🚀字符操作函数
🚀内存操作函数
🚀时间/ 日期函数
🚀数学函数
🚀其他库函数

注:使用库函数,必须包含 #include 对应的头文件。

http://en.cppreference.com (英文版)
http://zh.cppreference.com (中文版)
www.cplusplus.com           
以上是学习库函数的网站

2.2  自定义函数

C语言中的库函数也不是万能的,这也就衍生出了更加重要的自定义函数 。 自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是我们自己来设计,来赋予他的“使命”,这给了程序员很大的发挥空间!

函数的组成:

ret_type fun_name(para1, * )
{
 statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1    函数参数

下面举一个实例(得到最大的哪个数): 

#include <stdio.h>
 //输入两个数,得到最大的数
int get_max(int x, int y) {
 return (x>y)?(x):(y);
 //exp1 ? exp2 : exp3(条件操作符)  
 //如果x>y,输出x,否则输出y  
}
int main()
{
 int num1 = 10;
 int num2 = 20;
 int max = get_max(num1, num2);
 printf("max = %d\n", max);
}

3、函数的参数 

3.1 实际参数(实参):

🚀在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数”(简称“实参”)。

🚀实参可以是常量变量表达式, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参。 因此应预先用赋值,输入等办法使实参获得确定值。

3.2 形式参数(形参):

🚀是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.

🚀形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例。(分配内 存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

 下面用代码实例说明:输入两个数,交换两个数的值,并输出

#include <stdio.h>
//该代码段没有实现两数的交换
void Swap(int x, int y) {
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap(num1, num2);//自定义函数
	printf("num1 = %d\nnum2 = %d\n", num1, num2);
	return 0;
}

代码实例输出结果 

由此我们可以得出上面代码没有实现我们所想要的功能,这是怎么一回事呢,我们下面继续探讨(形式参数当函数调用完成之后就自动销毁了)

我们对上面的代码就行改进,下面代码实例: 

#include <stdio.h>
//该代码段实现了两数的交换
void Swap(int* x, int* y) {
	int tmp = 0;
	tmp = *x;
	*x = *y;
	*y = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap(&num1, &num2);//自定义函数
	printf("num1 = %d\nnum2 = %d\n", num1, num2);
	return 0;
}

  

下面我们来总结: 

🚀第一张代码图:(传过去的是值)调用函数Swap时,在Swap函数的内部,两个数的值时进行了交换,但是呢,x y是形参,形式参数当函数调用完成之后就会自动销毁,就是出了Swap函数的作用域后,x y就会被销毁,不会被保存,所以不会实现两个数交换的功能。

🚀第二章代码图:(传过去的是地址)有第一张代码图的分析后,也知道了形式参数当函数调用完成之后就会自动销毁,所以我们就传地址过去,把num1num2的内存地址传给xy,在Swap函数中,xy可以找到num1num2的内存地址,从而直接对num1num2进行修改。

3.3 用内存解释形参,实参

下面为代码实例: 

#include <stdio.h>
//不能完成要求
void Swap1(int x, int y) {
 int tmp = 0;
 tmp = x;
 x = y;
 y = tmp; 
}
//实现想要的结果
void Swap2(int *px, int *py) {
 int tmp = 0;
 tmp = *px;
 *px = *py;
 *py = tmp; 
}
int main()
{
 int num1 = 1;
 int num2 = 2;
 Swap1(num1, num2);
 printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
 Swap2(&num1, &num2);
 printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
 return 0; 
}

上面 Swap1 Swap2 函数中的参数 xypxpy 都是形式参数。在main函数中传给 Swap1 num1 , num2 和传 给 Swap2 函数的 &num1 &num2 实际参数

这里我们对函数的实参和形参进行分析: 

代码对应的内存分配如下: 

这里可以看到 Swap1 函数在调用的时候, x y 拥有自己的空间,同时拥有了和实参一模一样的内容。 所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝

4、函数的调用 

4.1 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。

4.2 传值调用

🚀传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
🚀这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

 下面来一个练习,写一个函数,实现一个整形有序数组的二分查找。

int binary_search(int arr[], int k, int sz)
{
	int left = 0;
	int right = sz - 1;//内存中是从0开始数
	while (left <= right)
	{
		int mid = left + (right - left) / 2;
		//这样写的原因是因为如果遇见超级大的数字,那么计算机就会出错
		//你也可以这样写 int mid = (left + right) / 2;
		if (k > arr[mid])
			left = mid++;
		else if (k < arr[mid])
			right = mid--;
		else
			return mid;
	}
	return -1;
}
int main(){
	int k = 0;
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	scanf("%d", &k);
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = binary_search(arr, k, sz);
	if (-1==ret)
		printf("找不到");
	else
		printf("找到了,k的位置为%d", ret);
	return 0;
}

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

函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。 

5.1 嵌套调用

下面代码实例:

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

函数可以嵌套调用,但是不能嵌套定义,下面代码实例(错误示范) 

int main()
{
	test()
	{
		;
	}
}

 5.2 链式访问

把一个函数的返回值作为另外一个函数的参数。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[20] = "hello";
	int ret = strlen(strcat(arr, "bit"));//strlen:计算\0之前字符串的个数;strcat:将bit叠加到 
                                         //arr数组中
	printf("%d\n", ret);
	return 0;
}

 6.函数的声明与定义

声明: 

1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数
声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
3. 函数的声明一般要放在头文件中的

定义:

函数的定义是指函数的具体实现,交待函数的功能实现。


总结

函数的递归与经典题目放在下一篇文章。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霄百

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值