深入浅出C语言——函数

深入浅出C语言——函数本文详细介绍了 C 语言中的函数 包括函数分类 库函数与自定义函数 参数类型 实际参数与形式参数 函数调用方式 传值与传址 函数的声明与定义 递归函数的概念及应用

大家好,欢迎来到IT知识分享网。


一、函数

  • 函数负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
  • 函数有输入参数并有返回值,提供对过程的封装和细节的隐藏

二、C语言中函数的分类

在C语言中函数可以分为两大类:

  1. 库函数
  2. 自定义函数

1. 库函数:

  • 类似printfscanfstrlen等函数具有基础功能,在开发的过程中每个程序员都可能用的到,为了支持可移植性提高程序的效率,C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
  • 随着C语言的发展,把常有的一些功能实现成函数,集成为库,由C语言直接提供,可以让给我们代码开发效率变高,更加标准。

C语言常用的库函数

  • IO函数
  • 字符串操作函数
  • 字符操作函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 其他库函数

在使用库函数时,必须包含#include对应的头文件。使用C语言库函数的经典网站http://www.cplusplus.com/ 可以查询到相应库函数的使用规则(阅读文档时,要注意文档中的null表示\0)。


2. 自定义函数

  自定义函数是由用户自己设计,自定义函数也有函数名,返回值类型和函数参数。函数的功能其实可以类似比喻为一个工厂,对于原材料(输入参数)加工得到产品(返回值)。

自定义函数的语法结构

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

三、函数的参数

1. 实际参数

  • 实际参数简称为实参,是真实传给函数的参数。
  • 实参可以是常量、变量、表达式、函数等。
  • 在进行函数调用时,实参必须有确定的值,以便把这些值传送给形参。

2. 形式参数

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

3. 函数传参时的陷阱

在这里插入图片描述

修改后,正确的代码应该为:

在这里插入图片描述

  • 当实参传给形参的时候,形参是实参的一份临时拷贝,对形参的修改不会影响实参。
  • 如果自定义的函数只是想获得main函数的某些时,这时使用传值传参就可以。
  • 如果这个函数与main函数的某些变量需要建立联系的时候才用传地址传参

四、函数的使用

1. 传值调用

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

2.传址调用

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

五、练习自定义函数

判断一个数是不是素数

//素数一般指质数。 质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。 #include<math.h> #include<stdio.h> int is_prime(int n) { 
    // 用2~n-1数字去试除或者2~sqrt(n)试除 for ( int i = 2; i <= sqrt(n); i++) { 
    if (0 == n % i) return 0; } return 1; } int main() { 
    int n = 0; scanf("%d", &n); if (1 == is_prime(n)) printf("%d是素数", n); else printf("%d不是素数", n); return 0; } 

判断一年是不是闰年

//闰年被4整除,并且不被100整除,或者被四百整除 int is_leap_year(int y) { 
    //是闰年返回1,不是返回0 if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) return 1; else return 0; } int main() { 
    int y = 0; scanf("%d", &y); if (1 == is_leap_year(y)) printf("%d年是闰年", y); else printf("%d年不是闰年", y); return 0; } 

实现一个整形有序数组的二分查找

#include<stdio.h> //找到了就返回下标,//找不到就返回-1 int binary_search(int arr[], int k, int sz) { 
    int left = 0; int right = sz - 1; while (left <= right) { 
    int mid = left + (right - left) / 2; if (arr[mid] < k) { 
    left = mid + 1; } else if (arr[mid] > k) { 
    right = mid - 1; } else { 
    return mid; } } return -1;//找不到 } int main() { 
    int arr[] = { 
    1,2,3,4,5,6,7,8,9,10 }; int k = 0; scanf("%d", &k); int sz = sizeof(arr) / sizeof(arr[0]); int ret=binary_search(arr, k,sz); if (-1 == ret) printf("找不到"); else printf("找到了,下标是%d\n", ret); return 0; } 
  • 这行代码int sz = sizeof(arr) / sizeof(arr[0]);一定要放到函数外面,因为数组在传参的时候,传递的是首元素的地址
  • 因为 int arr[ ] 在传参的时候其实传过去的是 int* arr,所以[ ]不需要指定大小。

每调用一次这个函数,就会将 num 的值增加1

//因为要改变main函数的变量,所以这里使用传址调用 void test(int* p) { 
    *p += 1; } int main() { 
    int num = 0; test(&num); printf("%d\n", num); return 0; } 
  • 在这个函数中,值得注意的是运算符的优先级,++的优先级更高,不能写成*p++

建议

  • 在写自定义函数的时候,建议先去写函数的使用方法,再写函数主体内容。
  • 这种方式在编程中称为测试驱动开发(test driven development)。
  • 另外,建议设计的函数的功能尽可能单一。

六、函数嵌套调用与链式访问

1. 嵌套调用

  • 函数和函数之间可以根据实际的需求进行组合的,相互调用的。
  • 函数可以嵌套调用但是不能嵌套定义。

2. 链式访问

  • 链式调用就是把一个函数的返回值作为另一个函数的参数。
#include <stdio.h> int main() { 
    printf("%d", printf("%d", printf("%d", 43))); //打印出4321 //注:printf函数的返回值是打印在屏幕上字符的个数 return 0; } 

七、函数的声明和定义

1. 函数的声明

  • 函数声明告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是不是存在,函数声明决定不了。
  • 函数的声明一般出现在函数的使用之前,要满足先声明后使用,在函数声明时,函数形参的名字可以省略,例如:int Add(int ,int )
  • 函数的声明一般要放在头文件中的,自己写的头文件用双引号include"add.h"
  • 在多人协同开发时,如果不愿意暴露代码可以编译成静态库,声明暴露给其他人,但是隐藏函数的实现方式。

2. 函数定义

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

八、函数递归

1. 递归的概念

  • 程序直接或间接调用自身的编程技巧被称为递归,递归做为一种算法在程序设计语言中广泛应用。
  • 它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
  • 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
  • 递归=递推+回归,主要思考方式在于:把大事化小

2. 递归的两个必要条件

  • 递归一定存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。

3. 递归练习

接受一个整型值(无符号),按照顺序打印它的每一位。

在这里插入图片描述


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

在这里插入图片描述


不考虑溢出求n的阶乘

在这里插入图片描述


不考虑溢出求第n个斐波那契数

在这里插入图片描述

在使用 fib 这个函数的时特别耗费时间。可以发现 fib 函数在调用的过程中很多计算其实在一直重复。而且如果你的参数比较大,那就会报错: stack overflow(栈溢出)这样的信息。

系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出

解决方案:非递归的方式来实现

在这里插入图片描述

许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。但是这些问题的迭代实现往往比递归实现效率更高,当一个问题相当复杂,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/156763.html

(0)
上一篇 2025-02-10 14:45
下一篇 2025-02-10 15:00

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信