C语言-入门-基础-语法-函数(八)

x33g5p2x  于2022-07-10 转载在 其他  
字(5.1k)|赞(0)|评价(0)|浏览(346)

程序是由一个个函数组成的。我们之前虽然没有正式介绍函数,但是我们早已经开始使用函数了。因为离开了函数,我们的程序没有办法正常的工作。只不过我们使用的是 C 语言内置的标准函数库。

什么是函数

函数就是一段封装好的,可以重复使用的代码,它使得我们的程序更加模块化,不需要编写大量重复的代码。函数可以提前保存起来,并给它起一个独一无二的名字,只要知道它的名字就能使用这段代码。函数还可以接收数据,并根据数据的不同做出不同的操作,最后再把处理结果反馈给我们。 在 C 语言中,我们必须要包含一个函数,就是我们最开始介绍的 mian 函数。

例如: 你用C语言编写了一个MP3播放器程序,那么它的程序结构如下图所示:

可以说C程序的全部工作都是由各式各样的函数完成的,所以也把C语言称为函数式语言

为什么需要函数?

  1. 函数帮助我们可以减少代码的冗余,也就是不用为了实现一个相同的功能而把这个功能的代码分别粘贴在这个功能出现的地方。
  2. 如果没有函数的话,代码要是一两行还好,要是比较复杂,那么所占用的篇幅将会非常巨大。有了函数,我们只要编写一次实现这个功能,在需要使用这个功能的地方直接调用这个函数就可以,由原来的可能成百上千行的粘贴直接变为了一行的调用。
  3. 如果这个常用的功能要进行修改。在粘贴的情况下,你要修改你粘贴过的每一个地方。这不仅仅是耗时费力,还有可能出现遗漏。
  4. 函数的使用还能使得程序更易于阅读与维护。使用函数会让程序变得模块化。每个函数是一个实现一个完整功能的模块。当我们需要开发新的程序的时候,只要按照需要把每个模块拼接在一起就可以。
  5. 当程序出现错误的时候。我们也可以很快的分别测试每个模块,而不是通过在一个非常长的程序中通过断点测试的方式一点点进行。
  6. 函数同时还提供了程序的抽象化,也就是我们不需要关注一些函数内部的工作方式就可以使用他们。比如我们最常用的屏幕打印函数,这个函数非常常用。使用这个函数是要根据函数名直接调用就可以了,不用了解内部是如何工作的。这让你更加专注于你的程序功能的实现,而不是别人的程序的工作情况,可以极大的提高你的程序的开发效率。

函数是现代编程语言中的重要组成部分。肩负着实现程序功能的职责。是程序的基本组成单元。好的函数往往只实现简单的单一的功能,而不是大而全的万金油。要尽量的瘦身。

函数的分类

在C语言中可从不同的角度对函数分类
从函数定义的角度看,函数可分为库函数和用户定义函数两种

  • 库函数: 由C语言系统提供,用户无须定义,也不必在程序中作类型说明,只需在程序前包含有该函数原
    型的头文件即可在程序中直接调用。在前面各章的例题中反复用到printf、scanf、getchar、
    putchar等函数均属此类
  • 用户定义函数: 由用户按需编写的函数。对于用户自定义函数,不仅要在程序中定义函数本身,而且在
    主调函数模块中还必须对该被调函数进行类型说明,然后才能使用

从函数执行结果的角度来看, 函数可分为有返回值函数和无返回值函数两种

  • 有返回值函数: 此类函数被调用执行完后将向调用者返回一个执行结果,称为函数返回值。(必须指
    定返回值类型和使用return关键字返回对应数据)
  • 无返回值函数: 此类函数用于完成某项特定的处理任务,执行完成后不向调用者返回函数值。(返回值
    类型为void, 不用使用return关键字返回对应数据)

从主调函数和被调函数之间数据传送的角度看,又可分为无参函数和有参函数两种

  • 无参函数: 在函数定义及函数说明及函数调用中均不带参数。主调函数和被调函数之间不进行参数
    传送。
  • 有参函数: 在函数定义及函数说明时都有参数,称为形式参数(简称为形参)。在函数调用时也必须给
    出参数,称为实际参数(简称为实参)

函数的定义

函数头与函数体两部分组成 , 其中函数头部分包含函数的返回值类型,函数名,函数的参数, 函数体部分由实现函数功能的一条至多条语句组成。

定义函数的步骤:

  1. 函数名: 函数叫什么名字
  2. 函数体: 函数是干啥的,里面包含了什么代码
  3. 返回值类型: 函数执行完毕返回什么

注意: 在c语言中函数是没有重载的,也就是同一个.c文件内部定义的函数名称不能相同

无参无返回值函数定义

格式:

void 函数名() { 
	函数体;
}

列:

void  printRose() {
    printf("Hello, World!\n");
}

注意: 如果函数不需要返回数据给调用者, 那么函数中的return可以不写 ,在合适的时候使用return可以用于直接结束当前函数

无参有返回值函数定义

格式:

返回值类型 函数名() { 
		函数体; 
	return 值;
}

列:

int getMax() {
    printf("请输入两个整数, 以逗号隔开, 以回车结束\n");
    int number1, number2;
    scanf("%i,%i", &number1, &number2);
    int max = number1 > number2 ? number1 : number2;
    return max;
}

有参无返回值函数定义

形式参数表列表的格式: 类型 变量名,类型 变量2,......
格式:

void 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
	 函数体;
 }

列:

void printMax(int value1, int value2) {
    int max = value1 > value2 ? value1 : value2;
    printf("max = %i\n", max);
}

有参有返回值函数定义

格式:

返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) { 
		函数体; 
	return 0;
}

列:

int printMax(int value1, int value2) {
    int max = value1 > value2 ? value1 : value2;
    return max;
}

形参和实参

形参

在定义函数时,函数名后面小括号()中定义的变量称为形式参数,简称形参, 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效,函数调用结束返回主调函数后则不能再使用该形参变量

int max(int number1, int number2){  // 形式参数
    return number1 > number2 ? number1 : number2;
}

实参

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

形参、实参注意事项

  • 调用函数时传递的实参个数, 必须和函数的形参个数必须保持一致

  • 形参和实参的类型如果不一致, 会自动转换为形参类型,但是必须满足自动转换的条件,否则就会报错

  • 当使用基本数据类型(char、int、float等)作为实参时,实参和形参之间只是值传递,修改形参的值并不影响到实参函数(如果是引用类型那么就会影响了)

返回值注意事项

  • 如果没有写返回值类型,默认是int

  • 函数返回值的类型和return实际返回的值类型应保持一致。如果两者不一致,则以返回值类型为准,
    自动进行类型转换(如果类型自动转换失败那么就会报错)

  • 一个函数内部可以多次使用return语句,但是return语句后面的代码就不再被执行

函数的声明

在C语言中,函数的定义顺序是有讲究的, 默认情况下,只有后面定义的函数才可以调用前面定义过的函数, 如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明, 否则系统搞不清楚有没有这个函数, 系统搞不清楚这个函数接收几个参数,系统搞不清楚这个函数的返回值类型是什么, 所以函数声明,就是在函数调用之前告诉系统, 该函数叫什么名称, 该函数接收几个参数, 该函数的返回值类型是什么

函数的声明格式: 将自定义函数时{}之前的内容拷贝到调用之间即可,一般在当前文件所有函数前面位置
例如: int max( int a, int b ); int max( int, int );

函数声明仅仅代表着告诉系统一定有这个函数 ,和把这个函数的详细信息告诉系统, 让系统知道这个函数具体的业务逻辑是怎么运作的

函数声明注意事项:

  1. 函数的实现不能重复, 而函数的声明可以重复 (没啥卵用)
  2. 函数声明可以写在函数外面,也可以写在函数里面, 只要在调用之前被声明即可(一般都写在文件开头位置,所有函数之前)
  3. 当被调函数的函数定义出现在主调函数之前时,在主调函数中也可以不对被调函数再作声明(不建议)
  4. 如果被调函数的返回值是整型时,可以不对被调函数作说明,而直接调用(不建议)

main函数扩展

main是函数的名称, 和我们自定义的函数名称一样, 也是一个标识符只不过main这个名称比较特殊, 程序已启动就会自动调用它
return 0; 如果main函数的执行正常, 那么就返回0, 如果main函数执行不正常, 那么就返回一个非0的数

以上我们根据之前学的,都能明白, 在main函数中还有一个内容非常重要,就是main函数的参数,main函数有三个参数argc、argv和envp

  • int argc用于存放命令行参数的个数。 (第一个代表他自己)
  • char *argv[]是个字符串的数组,每个元素都是一个字符指针,指向一个字符串,即命令行中的每一个参数。
  • char *envp[]也是一个字符串的数组,这个数组的每一个元素是指向一个环境变量的字符指针。(在实际开发中,envp参数的应用很少,各位了解一下就行了。)
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[], char **env) {
    int i = 0;
    printf("******************* Begin argv *******************\n");
    printf("argc: %d\n", argc);  //程序名称; =1 只有一个

    for (i = 0; i < argc; i++) {
        printf("%s\n", argv[i]); //指向输入的程序路径及名称

    }

    printf("******************* End argv *******************\n");

    printf("\n");

    printf("\n");

    printf("******************* Begin env *******************\n");

    for (i = 0; env[i] != NULL; i++) {
        printf("%s\n", env[i]); //为环境变量
    }

    printf("******************* End env*******************\n");

    printf("\n");
    system("pause");
    return 0;
}

参数1是启动程序, 之后就是实际的参数,使用空格隔开

一般会这样使用,第三个参数基本不会用到的

int main(int argc, char *argv[]) {

    return 0;
}

递归函数

什么是递归函数? 函数递归就是函数自己调用自己。(自己搞自己)
那么递归的好处是什么呢? 它能够将复杂的问题,用少量的代码来表示,增加了代码的可读性。

递归的两个必要条件:

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

列: 按照顺序打印一个数的整形值

#include <stdio.h>

int  print(int n) {
    if (n > 9) {//递归结束条件
        print(n / 10);
    }
    printf("%d", n % 10);
}

int main() {
    int num ;
    printf("请输入一个整数:");
    scanf("%d", &num);
    print(num);
    return 0;
}

解析: 看懂下图你递归就入门了

点赞 -收藏-关注-便于以后复习和收到最新内容有其他问题在评论区讨论-或者私信我-收到会在第一时间回复感谢,配合,希望我的努力对你有帮助^_^

免责声明:本文部分素材来源于网络,版权归原创者所有,如存在文章/图片/音视频等使用不当的情况,请随时私信联系我。

相关文章