白话C语言之函数

简介: 白话C语言之函数

大家好这里是三岁,C语言是各类语言的基础,由于个人学习需要特别开设了C语言基础专题,内容不一定深入,都是基础知识,还是白话C语言,最简单的方式带领大家学习不是那么容易理解的C语言!


函数



什么是函数呢???

和数学里面的一样?y=kx+b?

我认为在编程中这种理解不一定对。整个编程的内容都算是数学中的函数,都是数据处理的过程。(个人理解)

那么我们说的函数是什么呢?


我认为是编程人员偷懒的法宝!

记得编程届有这样子一个说法:一段代码只重复3遍以上就应该封装成一个函数,这样子可以减少很多代码量。


只要把一些具有相同规律的内容或具象化的模块进行打包封装形成一个函数,这样子就可以简单的调用,增加代码的可读性同时易于维护。


在C语言中函数具有两方面的用途:


  • 第一种:字面意思:即求得一个函数的结果。
  • 第二种:完成一个具体的功能,相当于一个子函数。


要求一个函数的结果的值或者要运行该函数来完成相应的功能,则称该函数被掉用或调用了该函数。


函数的分类


1、用户使用角度:

  • 标准函数(库函数):
    系统定义的不需要用户自己去定义,可以直接使用他们。如:sin()、sqrt()、fabs()
  • 用户自定义函数:用户为了特定需求去完成的函数。


2、从函数类型分类

  • 无参函数:函数定义的括号中没有参数
  • 有参函数:函数名后面的括号没有参数


白话:两个函数的分类标准不一样

其实就是一个是系统写好了的可以直接使用。(类似于python中的自带类库函数)

另外一个就是自己或者开源开发者提供的(类似于第三方类库)

还有一种就是看看函数有没有输入数据,有就是有参的没有就是无参的

具体的还是接下去看看吧


函数的定义


组成:函数说明部分和函数体部分。

函数说明部分主要组成有函数类型、函数名和形式参数等三部分。

函数体主要就是函数内的局部变量的声明和执行语句组成。用来完成具体的功能。


  • 无参函数
类型说明符 函数名(){
    局部变量声明;
    函数体(语句执行部分);
}


举例:

void print_hello(){
    printf("hello world");
}


  • 有参函数
类型说明符 函数名(形参列表){
    局部变量声明;
    函数体(语句执行部分);
}


int max(int x, int y){
    int z;
    if(x>y){  //判断x和y
        z=x;  //成立就把x赋值给z
    }
    else{
        z=y;  //不成立就把y赋值给z
    }
    return z;  //返回z
}


  • 空函数
类型说明符 函数名(形参列表){
}


特殊的函数,实际意义,起到占位符的作用,待后续添加。

类似于python中的pass


说明

  • 函数类型可以是合法的数据类型说明符用于确实函数的类型
  • 函数名可以是任意的合法标识符(数字字母下划线,其中数字不可开头)
  • C语言可以有多个函数组成但是main()(主函数)有且只有一个
  • 函数之间可以相互调用,但是不能够嵌套不能够调用主函数(main())


形参和实参


名词解释:形参和实参

形参和实参直接白话吧

形参就是我们定义的函数后面括号里面的内容如上面max()里面的int x,int y一样他们有具体的定义和内容吗,但是他们门没有实际的意义,更加类似于一张板凳,上面有姓名标签,只能够对应的“人”坐上去。

实参:指的是调用函数过程中实际赋值给函数的值,如max(12,5)这里的12和5就代表实际参数。他们就是对应做到形参板凳上的“人”,当然数据类型要相同不然会报错或者结果出现偏差。


返回值 — return


如果函数调用过程中需要返回数据就需要使用return函数不然默认没有返回值。让我们看看返回值有哪些要求。

  • 函数没有说明类型的时候以整型处理
  • 返回值以定义的函数类型为准,不相同的进行强制转换(注:这里可能会产生数据偏差)
  • 如果不调用则没有返回值
  • 如果不使用返回值的话可以使用void类型进行定义


函数的调用


格式

函数名(实参列表)

  • 如果是无参函数不需要添加实参列表
  • 形参和实参需要一一对应,个数数据类型也要也要,实参之间需要用,隔开。

调用:需要函数名加括号有参的要加参数列表,这个函数可以是独立的语句,可以是一个函数的实参等

main{
    print_hello();
    max(2,max(5,6);
    c=max(12,11);
    print("%d",c)
}


结果:

hello world

6

12


调用函数的声明


当需要调用非本文件的函数时就需要使用调用声明。


如调用库函数:

需要在开头添加#include命令

例如

#include<stdio.h>


以上是非本文件的函数,如果是本文件的函数呢?

首先看是否在主函数以前,如果是则不需要,如果不是就需要进行提前声明(如果是整数和字符型数据则不需要!)。


举例:


#include<stdio.h>
int main()
{
    int x=12,y=3,z;  //定义数值及初始化值
    z=add(x,y);  //调用值
    printf("%d\n",z);  //输出结果
}
int add(int x,int y){  //定义add函数
  return x+y;  //返回结果
}

20210427203057955.png


这里自定义函数在主函数后面但是他是整型数据所以可以不需要重新定义。

如果把int类型改成float呢?


#include<stdio.h>
int main()
{
    int x=12,y=3,z;
    z=add(x,y);
    printf("%d\n",z);
}
float add(int x,int y){
  return x+y;
}


20210427203442937.png

发生了数据引用冲突。

解决办法一:


#include<stdio.h>
int main()
{
    int x=12,y=3,z;
    float add(int x,int y);  //添加定义
    z=add(x,y);
    printf("%d\n",z);
}
float add(int x,int y){
  return x+y;
}


20210427203625830.png


在main函数添加一个定义值,相对应做了一个全局的声明,然后就可以继续执行了。

方法二:


#include<stdio.h>
float add(int x,int y){
  return x+y;
}
int main()
{
    int x=12,y=3,z;
    z=add(x,y);
    printf("%d\n",z);
}


20210427203750572.png


把add函数的位置上调这样子的话就可以在使用前进行编译。

方法三:

#include<stdio.h>
float add(int x,int y);  //提前定义声明
int main()
{
    int x=12,y=3,z;
    z=add(x,y);
    printf("%d\n",z);
}
float add(int x,int y){
  return x+y;
}


2021042720452190.png


局部变量和全局变量


函数体内说明的变量只适用于该函数内,而且只在声明以后有效,这样子的被称为局部变量。

C语言规定如下:

  • main函数中定义的变量也是局部变量,只能够在主函数中有效。
  • 函数定义时的形式参数也是局部变量,只能够在说明他的函数中使用。
  • 不同的函数可以有相同的局部变量,他们不会相互影响,代表不同的对象
  • 在复合语句块中定义的变量只能够在复合语句块中有效。


白话:

没有进行全局变量的内容只有在所在的大括号(花括号)中有效果,离开了大括号则无意义。需要再重新进行定义。


全局变量:需要再文档前进行声明,这样子,声明后面的全部范围都有效。

当局部变量和全局变量名字相同时只有局部变量有效


变量的存储类型


除了之前提到的数据类型还需要了解变量的存储类型。


存储类型 C语言声明词
动态存储 auto
静态存储 static
外部存储 extern
寄存器变量 register


如果用存储的时间来考虑就是静态存储动态存储

静态存储就相当于主席台上的位置,一场活动都是专属的座位,需要到活动结束以后才能够释放。

动态存储就好像是场地上的座位,按照需求进行一个动态的分配的,在人离开凳子后(函数调用结束后)系统就会收回然后分配给下一个需的人。


变量的存储类型声明:

存储类型 数据类型 变量名


1、自动变量(动态变量)(auto)

格式:

auto 数据类型 变量名


示例:

auto int a;


数据存储中如果没有使用存储类型的声明则默认为auto

auto存储在动态存储区,主要是指函数的动态局部变量形式参数

auto在函数调用开始分配内存,函数调用结束释放内存。

普通的内部变量均隐含的被认为是auto类型,一般很少使用auto特异说明。


2、局部静态变量(static)

格式:

static 数据类型 变量名


示例:

static int a;


静态变量存储在静态存储区,在其所在的程序和函数中是永久变量,如果在调用函数后想保留值继续下次使用可应该为静态存储。

示例:

#include<stdio.h>
int f(int b){
  auto int a=0;  // 定义动态变量 a 
  static int c=3;  // 定义静态变量c 
  a+=1;c+=1;  // a、c完成自加 
  return (a+b+c);  // 返回a、b、c的和 
}
int main(){
    int b=2,i;
    for(i;i<=3;i++){  // 循环 
        printf("%d\n",f(b));  // 输出f()的运行结果 
  }
}


次数 a的值 b的值 c的值 返回值
第一次开始 0 2 3 /
第一次结束 1 2 4 7
第二次开始 0 2 4 /
第二次结束 1 2 5 8
第三次开始 0 2 5 /
第三次结束 1 2 6 9
第四次开始 0 2 6 /
第四次结束 1 2 7 10


20210428000347583.png


解析:

a值是f()函数的局部动态变量。从上图可以看出 在f()函数结束以后内存释放,再次运行后重新赋值

c值是f()函数的局部静态变量、从上图可以看出 在f()函数结束以后内存没有释放,再次运行后还保存了上次的数据b是主函数的局部动态变量,虽然是局部变量但是分为两部分。

  • 第一部分是主函数定义的局部动态整型变量
  • 第二部分是给f函数的实参,转变成了f函数内部的局部动态整型变量

两次变量由于主函数没有结束最后的结果也是没有发生改变,直到程序结束以后才释放内存。


3、寄存器变量(register)

格式:

register 数据类型 变量名


由于数据存储在运行内存运算速度会快的多,可以提高执行效率。所以有了寄存器变量。

要求:

  • 1、寄存器变量的类型:intchar 或者是指向这两种的指针。
  • 2、仅适用于函数的局部变量和函数的形式参数。
  • 3、C语言规定寄存器的数量不能够超过3个,多余的按照auto处理。


4、外部变量(extern)

格式:

extern 数据类型 变量名


在定义全局变量时如果没有特别声明,则默认为extern

系统只给extern类型分配一次变量,如果已经分配会寻找同名的数据进行处理,不会再次分配。


5、静态全局变量(static)

格式:

static 数据类型 变量名


这个和上面的区别类似于publicprivate外部变量是公共的而静态全局变量是私有的。


递归函数



递归函数就是函数直接或间接调用自己形成类似于套娃的内容。如果设计得当是一个比较好的程序,设计不好,那就是bug聚集地和死循环乐园,容易被老板开除。


需要满足下面3个条件:

  • 原问题能够向另外一个更加简单的问题进行转换
  • 转换后的问题和原问题求解类似
  • 转换后能后有一个终止条件

举例:最经典的阶乘问题


#include<stdio.h>
long fac(int n){  //定义fac函数 
  long f;
  printf("n=%d\t",n);  //输出当前n的值(大->小) 
  if(n<0){
    printf("\nn<0,数据错误!");  //判断是否小于0 
    f=0;   //赋值为0 
  }
  else if(n==0)f=1;  //最后停止的标志 
  else f=fac(n-1)*n;  //数据没问题调用自己继续执行 
  printf("\n 返回:n=%d\tf=%d",n,f); //当调用到n==0后 开始执行 
  return f;
} 
main(){
  int n;
  long y;
  printf("需要输出的轮数:");
  scanf("%d",&n);
  y=fac(n);  //调用fac函数 
  if(y!=0){  //y!=0就输出 
    printf("\n%d!=%ld\n",n,y);
  } 
}


2021042801102934.png

该方法比for循环占用的时间更短,相对来说更加合适,但是和技术想法等都有关系。


就上面的函数停止条件是else if(n==0)f=1; //最后停止的标志这一句以后才停止。如果这里不把握好就会出现与预期不符的情况。


好啦,这次的函数就到这里,量比较大,如果是第一次学习语言,需要好好理解。相当于就是把经常用到的东西或者比较繁琐的东西单独处理,然后再到main函数进行组装的一个过程。


预告:下一课 :数组!

目录
相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
38 3
|
15天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
36 10
|
15天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
37 9
|
15天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
30 8
|
15天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
39 6
|
15天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
90 6
|
15天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
47 6
|
15天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
23 5
|
26天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
48 4
|
1月前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
32 6