初阶C语言 第二章-------《函数》知识点(自定义函数,库函数,函数递归)+思维导图+基本练习题+超详细+通俗易懂(建议收藏)

简介: 初阶C语言 第二章-------《函数》知识点(自定义函数,库函数,函数递归)+思维导图+基本练习题+超详细+通俗易懂(建议收藏)

绪论      

       书接上回,上回我们将C语言中的《控制语句》进行了详细的书写,这次我将在第二章写道《函数》,他不仅仅是名字和数学上的函数一样,其内涵其实是一样的!希望你可以通过我的这篇文章深刻的认识到函数的具体内容 (建议电脑观看) 。   image.png

所以安全带系好,发车啦(建议电脑观看)。  

思维导图:

image.png

               要XMind思维导图的话可以私信哈

目录


1.函数是什么?

2.库函数

3.自定义函数

4.函数的参数

4.1 实际参数(实参)

4.2:形式参数(形参)

5.函数的调用

5.1传值调用:

5.2传址调用:

5.3基本练习:

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

6.1:嵌套调用

6.2 链接访问

7.函数的声明和定义

7.1函数的声明:

7.2函数的定义:

8.函数的递归

9.迭代

1.函数是什么?

在维基百科中函数又被称为“子程序”:

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

而在C语言中函数分成了两类:库函数,自定义函数

附:主函数就是以 int main() ,大括号,return 0  组成的他在一个程序中是必须的,也是唯一的(只能有一个)

int main()

{

       语句;

       return 0;

}

2.库函数

知识点:

库函数就是我们在前面文章代码中直接有用到的,打印函数(printf),输入函数(scanf),字符串比较函数(strcmp),这些都是在日常编写程序时常常会用到的函数,也就是我们在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。

细节点:

注:既然不是自己写的,故在使用时就需要去'引'头文件#include....

如当你要打印(省略主函数)

#include<stdio.h>

printf("yyds");

像常用库函数还有如:

strlen(求字符串长度),字符串比较函数(strcmp)         :

memset()将数组前几位变成某一值       : #include<string>

system()调用VS自带的指令: #include<windows.h>

time()时间戳:#include<time.h>

srand() ,rand() 生成随机数: #include<stdlib.h>

pow()求次方,sqrt()开平方:#include<math.h>


.....


C语言中自己带的库函数还有很多很多若需要系统的了解其内涵则:

这推荐给你一个软件MSDN,和网站cplusplus.com(进入到旧版本),个人还是较喜欢用MSDN若有需要可以私信我。

使用方法:

MSDN:

1.如何搜索

image.png

2.该看的关键部分

参数形式,头文件,返回值

image.png附:


在开始会有对其功能的介绍

image.png

3.可以根据情况康康

下面还有:

Parameters(参数),Remarks(备注),example(举例)

image.png

cplusplus差不多,你可以根据我给的链接直达,在最上面搜索框中搜索

3.自定义函数

自己定义的函数,通过和库函数相同的方法,创建一个函数,传入参数,并返回一个值。程序员这个职业作用也体现于此,否则库函数如果可以作任何我们想完成的事,那肯定是不可能的(那还要程序员干嘛?),所以当遇到一些复杂独特的逻辑时,库函数就无法完成这时就一定需要程序员自己写一个函数来完成特定的任务。


他的大概语法是:

return_type function_name(para1,...) //return_type返回类型,function_name函数名称
                                     //para1函数参数
{
    语句;
}

附:

void test (void)//不要参数,当有参数传进则会提示警告

{

       ...;

}

int main()

{

       test(10);

}


下面以一些题目来展示自定义函数

基本练习:

1.找最大值

#include<stdio.h>
int max(int a,int b)
{
    return (a>b?a:b);//条件操作符先对1表达式进行判断若成立则进入2表达式(a)否则进入3表达式
}
int main()
{
    int x = 0;
    int y = 0;
    int max = max(x,y);
    printf("%d",max);
    return 0;
}

4.函数的参数

4.1 实际参数(实参)

简单来说就是函数传进去的参数,

如:max()中传进去的x,y 即max(x,y)

附:实参可以是常量,变量,表达式,函数,地址....,但一定得是一个具体的值

4.2:形式参数(形参)

形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(具体的实例化:分配内存单元这我会再写一篇博客...),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了(类似局部变量),因此形式参数只在函数中有效。

即max()中的max(int a, int b);

附:传值调用时的形参实例化之后其实相当于实参的一份临时拷贝。

5.函数的调用

知识点:

5.1传值调用:

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

5.2传址调用:

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操 作函数外部的变量(实参)(通过指针找到内存空间)

以下例子为例:

#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("%d %d\n", num1, num2);
  Swap2(&num1, &num2);
  printf("%d %d\n", num1, num2);
  return 0;
}

5.3基本练习:

用函数写一个二分查找(折半查找),递增的数组

 #define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Cheak(int* ar, int lef , int ri , int ke )//在形参部分起的名称可以和实参一样也可以不一样但尽量有意义
{
  while (lef <= ri)
  {
    int mid = (ri + lef) / 2;
    if (ke < *(ar+mid))
    {
      ri = mid - 1;
    }
    else if (ke > *(ar + mid))
    {
      lef = mid + 1;
    }
    else
    {
      return *(ar + mid);
    }
  }
  return 0;
}
int main()
{
  int arr[] = { 1,3,5,7,9,11,13,15,17,19};
  int k = 0;
  int l = 0;
  printf("输入一个数,我来帮你找到它");
  scanf("%d", &k);
  printf("\n");
  int r = sizeof (arr) /sizeof(arr[1]) -1;
  int num = Cheak(arr, l, r, k); // arr为首元素的地址,虽然如此但在形参接收时也可以直接用int arr[] 代替int * ar ,此处就直接用第二种了
                   //但在初学时尽量用第一种,int arr [1] =  *(arr + 1) 注意点+1一定要加括号 
  if (num == 0)
    printf("不在范围内\n");
  else
    printf("找到了 k  = %d", num);
  return 0;
}

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

知识点:

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

附:函数可以嵌套调用,但是不能嵌套定义(不能同时在一处创建两个函数块)。

6.2 链式访问

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

#include <stdio.h>
int main()
{
    printf("%d", printf("%d", printf("%d", 43)));
    return 0;
}

printf:

image.png

上面大概意思是:这个函数返回打印的字符个数,如果发生错误则返回负值。

故上面的结果应该是----评论区

7.函数的声明和定义

7.1函数的声明:

image.png

      当check函数放在了主函数后边时,就会出现一个警告Check未定义,因为程序是从上往下一步步看的。故此时若不想有警告则可以声明(或者将函数定义放在调用之前面(此处主函数之前)

声明的写法:告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。即  可以大概写成  int Add(int , int ) ,参数内有无名字都可以

附:一般可以把函数的声明放在头文件中,当你要调用的时候在包含一个自己的头文件即可#include<game.h>(此处可以等我后面更到用代码写小游戏时看出),即实现分模块开发,这样还可以利于假如想保留你的知识产权就可以进行代码的隐藏

7.2函数的定义:

自己写的函数,里面有具体的实现,交代函数功能

int Add(int a ,....)

{

       code;

}

8.函数的递归

知识点:

递归:一种程序调用自身的编程技巧

这种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略,即把大事化小,进行着重复的过程

细节点:

对于递归:因为如果用了递归他就会持续的调用自己,这个方法在每次的调用中都会开辟一个空间给这个函数(有就是堆栈)这种方法可以虽然可以大事化小,但同时也存在隐患(无限的开辟空间就可能会导致栈没占满而导致的栈溢出),所以我们在使用递归方法时要限制一个量然他不会无限递归下去(也就是一个限制条件),同时在每次递归后都要不断的接近这个限制条件

附:递归虽好,但也未必一定每次写代码都要用到它,只要自己写的代码简洁高效就是最好的代码,即(要注意递归的使用并不如何地方都适用)

递归的优点和缺点:

1.许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。

3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

下面我们用一些题目来对递归这个用一个更深刻的认识

习题练习:

输入一个整形,按照顺序打印他的每一位

如输入1234,打印1 2 3 4

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
1.
void leave(int a) 
{
  if (a / 10 != 0)  //当a / 10 不等于0说明他是一个大于10的数
  {
    leave(a / 10);
  }
  printf("%d ", a % 10);//打印
  //当递归回来时就会下来打印
}
#include<stdio.h>
int main()
{
  int a = 0;
  scanf("%d", &a);
  leave(a);
  return 0;
}
2.
int leave(int a)
{
  if (a / 10 == 0)//先设置限制条件,到个位
  {
    printf("%d ", a % 10);
    return 0;//当进到限制内就会开始返回
  }
  leave(a / 10);
  printf("%d ", a % 10);//返回后就会下来打印  当第一次返回时会变12余(莫)2并打印
  return 0;//此处可以不写但会有一个警告而已
}
#include<stdio.h>
int main()
{
  int a = 0;
  scanf("%d", &a);
  leave(a);
  return 0;
}

用递归实现strlen

int Strlen(char a[])
{
  if (*a == '\0')
    return 0;
  else
    return 1 + Strlen(a+1);//a+1就会时指针向后跳一个字符,a是首元素地址,故
}
int main()
{
  char a[] = "abcd";
  int n = Strlen(a);
  printf("%d", n);
  return 0;
}

递归实现裴波那契数(生兔子)

1  1 2 3 5 8 13  ....

int Fac(int x)
{
  if (x <= 2)//因为前俩个无法相加
  {
    return 1;//当减到小于等于2时就直接为1并返回
  }
  else
  {
    return Fac(x - 2) + Fac(x - 1);//加上前两个数
  }
}
int main()
{
  int i = 0;
  scanf("%d", &i);
  int n = Fac(i);
  printf("%d", n);
  return 0;
}

如:当输入第3为时,Fac(x-2 == 1) == 1  | Fac(x-1 == 2)== 1  因为x = 1, 2,故会退出递归

而当输入为5时,第一次 x-2 == 3 ,x-1 == 4  ,再进行x-2,与x-1他就会继续递归下去直到x-1,x-2  他们小于等于2时才会跳出

那当为6时他们第一次x-2 == 4  x-1 == 5  , 然后4 和 5 要继续分, 4变成 2 3,5 变成4,3 ,然后能分的还要继续递归。

从上面这些较小的数中就已经可以发现,这样的递归是不够效率的他的步骤不断的重复进行。

这样的话,当输入一个较大的数值时,他就一定会因为空间的不足,而栈溢出出错。

故就需要考虑改变把递归,变成迭代了

9.迭代

大概意思也就是一种非递归的形式。

接上裴波那契数递归会导致栈溢出,那么就改成迭代形式即:

int Fac(int x)
{
  int a = 1;
  int b = 1;
  int sum = 0;
  for (; x > 2; x--)
  {
    sum = a + b;
    a = b;
    b = sum;
  }
  return sum;
}
int main()
{
  int i = 0;
  scanf("%d", &i);
  int n = Fac(i);
  printf("%d", n);
  return 0;
}

本章完。预知后事如何,暂听下回分说。

相关文章
|
6天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
21 6
|
19天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
25天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
53 7
|
22天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
19 0
|
22天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
18 0
|
C语言
我们要掌握好多少C语言知识点才能做好C语言项目?
我们要掌握好多少C语言知识点才能做好C语言项目?
1251 0
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
33 3
|
26天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
34 10
|
25天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
29 4
|
30天前
|
存储 编译器 C语言
C语言函数的定义与函数的声明的区别
C语言中,函数的定义包含函数的实现,即具体执行的代码块;而函数的声明仅描述函数的名称、返回类型和参数列表,用于告知编译器函数的存在,但不包含实现细节。声明通常放在头文件中,定义则在源文件中。