【C语言】函数与递归

article/2025/9/12 2:29:23

目录😊

1. 函数是什么🐾

2. 库函数🐾

3. 自定义函数🐾

4. 函数参数🐾

5. 函数调用🐾

6. 函数的嵌套调用和链式访问🐾

7. 函数的声明和定义🐾

8. 函数递归🐾


1. 函数是什么

数学中我们常见到函数的概念。但是你了解C语言中的函数吗?

维基百科中对函数的定义:子程序

  • 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组 成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
  • 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软 件库。

2. 库函数

为什么会有库函数?

  1. 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想 把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格 式打印到屏幕上(printf)。
  2.  在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。
  3. 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。

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

学习库函数的网站:www.cplusplus.com

 简单的来讲,C语言常用的库函数有:

 strcpy函数:

#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = "hello world";char arr2[20] = { 0 };strcpy(arr2, arr1);printf("%s", arr2);return 0;
}

运行结果:

  memset函数:

#include<stdio.h>
#include<string.h>
int main()
{char arr[] = "hello world";memset(arr, 'x', 5);printf("%s", arr);return 0;
}

运行结果:

3. 自定义函数

如果库函数能干所有的事情,那还要程序员干什么? 所以更加重要的是自定义函数。 自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间

自定义函数和库函数一样,有函数名,返回值类型,函数参数

我们举一个例子:

写一个函数可以找出两个整数中的最大值。

int Max(int a, int b)
{return a > b ? a : b;
}

 无参和返回值的函数:

#include<stdio.h>
void test()
{printf("hehe\n");
}
int main()
{test();return 0;
}

写一个函数可以交换两个整型变量的内容:

 错误代码:

#include<stdio.h>
void Swap(int x, int y)
{int tmp = x;x = y;y = tmp;
}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);return 0;
}

这串代码并不能完成交换的操作:

 原因是为什么呢?通过调试,我们可以发现代码出错的原因:

我们发现啊a,b的地址和x,y的地址并不相同,当x,y的值交换后,a,b的值并没有发生改变

 

 在上述代码中,a,b是实参,x,y是形参

当实参传递给形参的时候,形参是实参的一份临时拷贝,所以对形参的修改不会影响实参.

解决方法:

#include<stdio.h>
void Swap(int *x, int *y)
{int tmp = *x;*x = *y;*y = tmp;
}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);return 0;
}

4. 函数参数

实际参数(实参)

真实传给函数的参数,叫实参。 实参可以是:常量、变量、表达式、函数等。 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

形式参数(形参)

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

5. 函数调用

传值调用

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

传址调用

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

练习

 写一个函数判断一个数是不是素数;

#include<stdio.h>
#include<math.h>
int is_prime(int n)
{int j = 0;for (j = 2; j <= sqrt(n); j++){if (n % j == 0){return 0;}}return 1;
}int main()
{int i = 0;for (i = 100; i <= 200; i++){if (is_prime(i) == 1){printf("%d ", i);}}return 0;
}

 写一个函数判断一年是否为闰年

#include<stdio.h>
int is_leap_year(int y)
{if ((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0))return 1;elsereturn 0;
}
int main()
{int i = 0;for (i = 1000; i <= 2000; i++){if (is_leap_year(i) == 1){printf("%d ", i);}}return 0;
}

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

#include<stdio.h>
int binary_search(int arr[],int k,int sz)
{int left = 0;int right = sz - 1;while (left <= right){int mid = (left + right) / 2;if (arr[mid] > k){right = mid - 1;}else if (arr[mid] < k){left = mid + 1;}else if (arr[mid] == k){return mid;}}return -1;
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int k = 5;int sz = sizeof(arr) / sizeof(arr[0]);int ret=binary_search(arr,k,sz);if (ret == -1){printf("找不到\n");}else{printf("找到了,下标是%d\n", ret);}return 0;
}


 

6. 函数的嵌套调用和链式访问

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

函数可以嵌套调用,但是不能嵌套定义。

嵌套调用:

#include<stdio.h>int test()
{printf("hehe\n");
}void fun()
{test();
}int main()
{fun();return 0;
}

链式访问:

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

#include<stdio.h>
#include<string.h>
int main()
{int len = strlen("abcdef");printf("%d\n", len);return 0;
}//换成链式访问👇#include<stdio.h>
#include<string.h>
int main()
{printf("%d\n", strlen("abcdef"));return 0;
}

7. 函数的声明和定义

函数声明:

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

函数定义:

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

 函数的定义也是一种特殊的声明,所以可以直接把函数定义放在main函数上面:

#include<stdio.h>
#include<string.h>int Add(int x, int y)
{return x + y;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = Add(a, b);printf("%d\n", c);return 0;
}

8. 函数递归

什么是递归?

程序调用自身的编程技巧称为递归( recursion)。

递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接 调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问 题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程 序的代码量。

递归的主要思考方式在于:把大事化小

递归的两个必要条件

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

练习:

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

例如: 输入:1234,输出 1 2 3 4

#include<stdio.h>
void print(n)
{if (n >= 10){print(n / 10);}printf("%d ", n % 10);
}
int main()
{unsigned int num = 0;scanf("%d", &num);print(num);return 0;
}

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

#include<stdio.h>
int my_strlen(char* s)
{if (*s == '\0'){return 0;}else{return 1 + my_strlen(s+1);}
}
int main()
{char arr[] = "abcdefg";int len = my_strlen(arr);printf("%d\n", len);return 0;
}

递归与迭代

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

#include<stdio.h>
int Fac(int n)
{if (n == 1)return 1;elsereturn n * Fac(n - 1);
}
int main()
{int n = 0;scanf("%d", &n);int s = Fac(n);printf("%d\n", s);return 0;
}

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

#include<stdio.h>
int Fib(int n)
{if (n == 1 || n == 2)return 1;elsereturn Fib(n - 2) + Fib(n - 1);
}
int main()
{int n = 0;scanf("%d", &n);int s = Fib(n);printf("%d\n", s);return 0;
}

使用这种方法效率很低,原因是有很多重复计算,可以使用下面的方法:

#include<stdio.h>
int Fib(int n)
{int a = 1, b = 1, c = 1;while (n>2){c = a + b;a = b;b = c;n--;}return c;
}
int main()
{int n = 0;scanf("%d", &n);int s = Fib(n);printf("%d\n", s);return 0;
}

提示:

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

总结:这篇文章主要写的是C语言中函数的使用方法,后期将继续写关于C语言的文章。如果我写的有什么的不好之处,请在文章下方给出你宝贵的意见。如果觉得我写的好的话请点个赞赞和关注哦~😘


http://chatgpt.dhexx.cn/article/pjSASBpy.shtml

相关文章

理解C语言递归函数的逐级返回(return)

递归函数&#xff0c;也即调用自身的函数。 C Primer Plus中有个例子很棒&#xff1a; /*理解C语言递归函数*/ #include<stdio.h> void up_and_down(int); int main(void) { up_and_down(1); return 0; } void up_and_down(int n) { printf("level %d: n loaca…

什么是递归函数?

文章目录 递归函数递归例题特点效率优点 递归函数 递归 递归就是一个函数在它的函数体内调用它自身。执行递归函数将反复调用其自身&#xff0c;每调用一次就进入新的一层。递归函数必须有结束条件。 当函数在一直递推&#xff0c;直到遇到墙后返回&#xff0c;这个墙就是结束…

关于C语言中的递归函数

递归实例&#xff1a; #include <stdio.h>void up_and_down(int);int main(void) { up_and_down(1);return 0;} void up_and_down(int n) {printf("Level %d: n location %p\n", n, &n); // #1if (n < 4)up_and_down(n 1);printf("LEVEL %d: n …

C语言递归函数求n!的两种写法---详细讲解

递归函数&#xff1a;在调用一个函数的过程中又出现直接或间接地调用该函数本身&#xff0c;称为函数的递归调用。 题目&#xff1a;求n!的阶乘,n需要手动输入一个数。 解题思路&#xff1a;假设我们是求5的阶乘5&#xff01;&#xff0c;那么我就要知道4&#xff01;等于多少…

C语言——函数的递归

函数的声明和定义 函数声明&#xff1a; 1.告诉编译器有一个函数叫什么&#xff0c;参数是什么&#xff0c;返回类型是什么&#xff0c;但是具体是不是存在&#xff0c;并不重要。 2.函数的声明一般出现在函数的使用之前&#xff0c;要满足先声明后使用。 3.函数的声明一般要…

【C语言】函数递归详解

函数递归 1. 什么是函数递归2. 递归的两个必要条件2.1 练习1&#xff1a;打印一个数的每一位2.2 练习2&#xff1a;求字符串长度&#xff08;strlen 模拟实现&#xff09; 3. 递归与迭代3.1练习3&#xff1a;求 n 的阶乘&#xff08;不考虑溢出&#xff09;3.1.1 递归求 n 的阶…

C语言详解:函数递归专题

文章目录 函数递归函数递归的定义和优缺点递归的使用场景及必要条件递归的细节说明递归的习题讲解1打印整数每一位输入输出示例解题思路代码逻辑 2递归和非递归求n阶乘输入输出示例解题思路代码逻辑 3strlen函数模拟输入输出示例解题思路代码逻辑 4逆序字符串输入输出示例解题思…

2021-11-03

**"21天好习惯"第一期—12**1.递归函数&#xff08;二&#xff09; 程序在计算5的阶乘的时候&#xff0c;先执行递推&#xff0c;当n1或者n0的时候返回1&#xff0c;再回推将计算并返回。由此可以看出递归函数必须有结束条件。 递归函数特点&#xff1a; 1.每一级…

[C语言学习]----函数递归(超详细!!!)

本篇介绍的是C语言函数递归的详细知识 程序的艺术来源于生活 目录 7. 函数递归 7.1递归是什么 7.2 递归的两个必要条件 7.2.1练习1&#xff08;详细讲解&#xff09; 7.2.2练习2&#xff08;详细讲解&#xff09; 7.3 递归与迭代 7.3.1练习3&#xff08;详细讲解&#xff…

五、需求分析建模之数据库建模

1. 了解E-R图在基于数据库的软件系统分析中的作用。 2. 复习并深化理解E-R图的相关概念。 3. 学习从实际应用问题中抽取E-R模型的方法。 4. 掌握简单ER图模型建模工具 E-R模型 Entity-Relationship Model E-R模型是一种数据建模的思想。 1. E-R模型的基本观点&#xff1a; 世…

数据库设计(1)—需求分析

2019独角兽企业重金招聘Python工程师标准>>> 需求分析是设计数据库的起点&#xff0c;需求分析结果是否准确反映用户的实际要求将直接影响到后面各阶段的设计&#xff0c;并影响到设计结果是否合理实用。 一、需求分析的任务 需求的任务是通过详细调查现实世界要处理…

SQL数据库设计(一)---需求分析与逻辑设计

今天先来介绍 数据库设计中的需求分析和逻辑设计(ER图)阶段&#xff0c;明天介绍物理设计与维护优化,数据库设计是非常有意思的:-) 数据库设计 根据系统业务的需要&#xff0c;结合我们所选用的DBMS&#xff0c;为这个业务系统构建出最优的数据存储模型。 并建立好数据库中的表…

数据库设计2————需求分析

需求分析任务 1、信息需求。明确数据库需要存储的数据&#xff0c;对这些数据将哪些梳理&#xff0c;同时还要描述数据间的联系。 2、处理需求。定义系统数据处理的操作功能&#xff0c;描述操作的优先次序。包括操作的执行频率和场合&#xff0c;操作与数据间的联系&#xff…

数据库设计(一) 需求分析

目前&#xff0c;大多数的应用系统都属于数据库应用程序&#xff0c;都离不开数据库的支持。数据库设计方案的优劣对于应用程序的运行至关重要。数据库设计过程就是针对具体的应用环境&#xff0c;设计优化的逻辑模式&#xff0c;并根据所采用的数据库系统设计物理结构&#xf…

三、数据需求与数据库设计

数据需求与数据库设计 数据需求 项目中主要包含了用户、权限&#xff08;菜单&#xff09;、角色三种类型的数据&#xff0c;各种数据包含的数据项如下&#xff1a; &#xff08;1&#xff09;用户&#xff1a;用户名、密码、生日、头像、简介、用户类型 &#xff08;2&…

SQL数据库实战需求分析→数据库设计

从这开始&#xff0c;就真正进入项目实战啦。先说点体会&#xff0c;我刚开始接触编程的时候&#xff0c;都是编写一些小东西&#xff0c;往往都是半天或者一天什么的就编完了&#xff0c;那时候根本没想过做程序之前还要有需求分析。经过快两年的学习&#xff0c;接触的都是比…

数据库性能需求分析及评估模型

数据库作为应用系统当中最重要的一块&#xff0c;也是性能测试非常关注的一块&#xff0c;根据我自己的项目经验&#xff0c;和以往对应用系统的性能需求分析和测试策略制定过程&#xff0c;总结一下如何开展数据库系统的性能需求分析&#xff0c;以及制定数据库能力评估模型。…

互联网应用开发实践:需求分析与数据库设计

在本文中将分析一个用于新生开学分配寝室的“宿舍秒杀”系统。从用户故事开始探索需求&#xff0c;进而分析得到系统的主要功能和非功能性需求。最后&#xff0c;根据需求分析设计数据库&#xff0c;数据库的设计原则是尽可能的方便之后的需求拓展和修改。 用户故事 用户故事一…

数据库应用系统的需求分析

一 需求分析的概念与意义 所谓的需求分析&#xff0c;就是对待开发系统要做什么&#xff0c;完成什么功能的全面描述 软件的一些特性使得需求的获取常常并不容易&#xff01; 比如软件功能复杂&#xff0c;需求可变性&#xff0c;软件的不可见性 二 获取需求的方法 面谈实地…

数据库设计:需求分析

设计一个性能良好的数据库系统&#xff0c;明确应用环境对系统的要求是首要的和基本的。因此&#xff0c;应该把对用户需求的收集和分析作为数据库设计的第一步。 需求分析的主要任务是通过详细调查要处理的对象&#xff0c;包括某个组织、某个部门、某个企业的业务管理等&…