• [要素察觉]C语言六讲:函数



    1. 函数的声明与定义 - 嵌套调用

    1 函数的声明与定义

    • 主函数调用其他函数,其他函数可以互相调用。

    函数需要提前声明:

    #include 
    
    //函数的声明与定义
    
    //这里是函数的定义
    //void print_message()
    //{
    //	printf("how do you do\n");
    //}
    
    //函数放在main函数下面需要提前声明
    void print_message();//print_message函数的声明
    
    int main()
    {
    	print_message();//调用print_message()
    	return 0;
    }
    
    void print_message()//放在下面时提示函数未声明,函数未声明时返回int类型
    {
    	printf("how do you do\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 添加.c文件 - 添加源文件
    • 添加.h文件 - 添加头文件

    新建头文件:

    #pragma once
    #include //<>是在标准库里找头文件
    #include 
    //在func.h中引用多个头文件,可以在源文件中直接引用func.h
    void print_message();//print_message函数的声明
    //在func.h中放入函数声明,源文件可通过引用func.h声明函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    #include "func.h"//""在当前目录下找自定义的头文件
    
    • 1

    自定义头文件中引用多个头文件,可以在源文件中直接引用自定义头文件
    < >是在标准库里找头文件
    " "在当前目录下找自定义的头文件
     
    在自定义头文件中放入函数声明,源文件可通过引用自定义头文件声明函数。

    【例1.1】函数嵌套调用

    func.h
    #include 
    #include 
    
    void print_message();//print_message函数的声明
    int print_star(int i);//printstar函数的声明,int i - 有形参,int - 有返回值
    
    func.c
    #include "func.h"
    
    int print_star(int i)//i - 即为形式参数
    {
    	printf("********************\n");
    	printf("printstar %d\n", i);
    	return i + 3;
    }
    
    void print_message() //可以调用printstar
    {
    	printf("how do you do\n");
    	print_star(3);//3是实参
    }
    
    main.c
    #include "func.h"
    int main()
    {
    	int a = 10;
    	a = print_star(a);//用一个变量接返回值
    	print_message();//调用print_message()
    	print_star(a);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    ctrl + 鼠标左键点击函数,就可以跳转到对应函数查看源码。

    C语言编译和执行的特点

    1. 一个项目可以有多个模块 - 源文件,便于分别编写、分别编译,进而提高调试效率。
    2. 程序编译时以源程序文件为单位而不是以函数为单位进行编译的。
    3. C程序的执行是从main函数开始的,如果在main函数中调用其他函数,那么在调用后会返回到main函数中,在main函数中结束整个程序的运行。
    4. 所有的函数的平行的,互相独立的,C语言中函数不能嵌套定义,可以互相调用。

    函数的声明与定义的差异

    1. 函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。
    2. 函数的声明的作用是把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。

    2 函数的分类与调用

    用户角度

    1. 标准函数:即库函数,由系统提供。
    2. 用户自定义的函数:解决专门需要。

    函数的形式

    1. 无参函数:一般用来执行指定的一组操作。在调用无参函数时,主调函数不向被调用函数传递数据。
       
      无参函数的定义形式如下:
    类型标识符 函数名()
    {
    	声明部分
    	语句部分
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 有参函数:主调函数在调用被调用函数时,通过参数向被调用函数传递数据。
       
      有参函数的定义形式如下:
    类型标识符 函数名(形式参数表列)
    {
    	声明部分
    	语句部分
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2. 函数的递归调用

    1 递归调用

    • 函数调用自身的函数称为递归函数,递归函数一定要有结束条件,否则会产生死循环
    • “递归的核心是找到基础公式”
      f ( n ) = n ∗ f ( n − 1 ) f(n)=n*f(n-1) f(n)=nf(n1)

    调试递归时多画图

    【例1.1】求n的阶乘

    #include 
    
    //求n的阶乘
    int f(int n)
    {
    	//一定要有结束条件,一定是写在return之前的
    	if (1 == n)
    	{
    		return 1;
    	}
    		
    	return n * f(n - 1);//写公式
    }
    
    int main()
    {
    	int n;
    	scanf("%d", &n);
    	printf("f(%d)=%d\n",n,f(n));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 使用递归在解决一些问题时,可以让问题变得简单,降低编程的难度

    【例1.2】爬楼梯

    假如有n个台阶,一次只能上1个台阶或2个台阶,请问走到第n个台阶有几种走法?
    举例说明如下:假如有3个台阶,那么总计就有3种走法:第一种为每次且1个台阶,上3次;第二种为先上2个台阶,再上1个台阶;第三种为先上1个台阶,再上2个台阶。

    要点:找递归关系
    s t e p ( n ) = s t e p ( n − 1 ) + s t e p ( n − 2 ) step(n)=step(n-1)+step(n-2) step(n)=step(n1)+step(n2)

    要达到n个台阶,只能从n-1个台阶走1步,或从n-2个台阶走2步。
    依次向前推。

    #include 
    
    //上台阶,到第n个台阶,有多少种走法
    
    int step(int n)
    {
    	if (1 == n || 2 == n)//但台阶是1个,或者是2个时,递归结束
    	{
    		return n;//1个台阶1种走法,2个台阶2种走法
    	}
    	return step(n - 1) + step(n - 2);
    }
    int main()
    {
    	int n; 
    	scanf("%d", &n);
    	printf("step(%d) = %d\n", n, step(n));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意

    1. 找公式
    2. 编写结束条件

    3. 局部变量与全局变量

    1 全局变量解析 - 形参 - 实参解析

    在不同的函数之间传递数据时,可使用的方法如下:

    1. 参数:通过形式参数和实际参数。
    2. 返回值:用return语句返回计算结果。
    3. 全局变量;外部变量。

    返回值:
    如果b调用a函数
    b称为调用函数
    a称为被调函数
    返回值只能是被调函数a返回给调用函数b

    【例1.1】全局变量的使用。

    #include 
    
    int i = 10;//i是一个全局变量,不建议使用
    
    void print(int a)
    {
    	printf("I am print i = %d\n", i);
    }
    
    int main()
    {
    	printf("main i = %d\n", i);
    	//i = 5;//接下来打印5
    	//int i = 5;//接下来打印10
    	print(5);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    全局变量

    有全局变量int i = 10;
    在主函数中写
    i = 5;改变的是全局变量i
    int i = 5;是局部变量,与全局变量i重名,全局变量i不改变。

    • 全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效。
    • 如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值。
    • 不建议使用全局变量。
    	{
    		int j = 5;
    	}//局部变量的有效范围是离自己最近的花括号
    	printf("%d\n",j);//无法打印!!
    
    	for(int k = 0; k < -1)
    	{
    	
    	}
    	printf("%d\n",k);//无法打印!!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 局部变量的有效范围是离自己最近的花括号,括号外无法使用。
    • for循环括号内定义的变量,循环体外不可用。建议将变量定义在循环括号外。

    【例1.2】改print(int i)

    把print(int a)改为print(int i),那么print函数的打印结果是多少?

    int i = 10;
    
    //把print(int a)改为print(int i),那么print函数的打印结果是多少?
    void print(int i)//形参可以看成一个局部变量
    {
    	printf("I am print i = %d\n", i);//输出3
    }
    
    
    int main()
    {
    	{
    		int j = 5;
    	}//局部变量的有效范围是离自己最近的花括号
    	printf("main i = %d\n", i);
    	//i = 5;//接下来打印5
    	//int i = 5;//接下来打印10
    	print(3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    将printf(int a)改为printf(int i),则不再打印全局变量10,而打印print(3)中的3。

    • 形参可以看成一个局部变量。

    关于形参与实参的一些说明

    1. 定义函数中指定的形参,如果没有函数调用,那么它们并不占用内存中的存储单元。
       
      发生函数调用时,形参分配内存单元。
      在调用结束后,被释放。

    2. 实参可以是常量、变量或表达式,但要求它们有确定的值。
       
      例如,print(i+3)在调用时将实参的值i+3赋给形参。
      print函数可以有多个形参,如 print(int a,int b)

    3. 在被定义的函数中,必须指定形参的类型。
       
      如果实参列表中包含多个实参,那么各参数间用逗号隔开。
      实参与形参的个数应相等,类型匹配,且按顺序对应,一一传递数据。

    4. 实参与形参的类型应相同或赋值应兼容。

    5. 实参向形参的数据传递是单向“值传递”,只能由实参传给形参,而不能由形参传回给实参。
       
      调用函数时,给形参分配存储单元,并将实参对应的值传递给形参;调用结束后,形参单元被释放,实参单元仍保留并维持原值。

    6. 形参相当于局部变量,因此不能再定义局部变量与形参同名,否则会造成编译不通。

    2 局部变量与全局变量

    1. 局部变量(内部变量)

    在一个函数内部定义的变量称为内部变量。它只在本函数范围内有效,即只有在本函数内才能使用这些变量,故也称局部变量。

    关于局部变量需要注意如下几点:

    1. 主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
    2. 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。
    3. 形式参数也是局部变量。
    4. 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称“分程序”或“程序块”。上文中的int j=5就是如此,只在离自己最近的花括号内有效,若离开花括号,则在其下面使用该变量会造成编译不通。
    5. 注意一个细节,for循环的小括号内定义的int i,在离开for循环后,是不可以再次使用的。

    2. 全局变量(外部变量)

    函数之外定义的变量称为外部变量。外部变量可以为本文件中的其他函数共用,它的有效范围是从定义变量的位置开始到本源文件结束,所以也称全局变量(全程变量)。

    关于全局变量需要注意如下几点:

    1. 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。
    2. 使用全局变量过多会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错,因此要有限制地使用全局变量(尽量不用)。
    3. 因为函数在执行时依赖于其所在的外部变量,如果将一个函数移到另一个文件中,那么还要将有关的外部变量及其值一起移过去。然而,如果该外部变量与其他文件的变量同名,那么就会出现问题,即会降低程序的可靠性和通用性。C语言一般要求把程序中的函数做成一个封闭体,除可以通过“实参→形参”的渠道与外界发生联系外,没有其他渠道。

    总结

    1.1

    • 主函数调用其他函数,其他函数可以互相调用。

    • 函数需要提前声明。

    • 自定义头文件中引用多个头文件,可以在源文件中直接引用自定义头文件

    • 在自定义头文件中放入函数声明,源文件可通过引用自定义头文件声明函数。

    • 一个项目可以有多个模块 - 源文件,便于分别编写、分别编译,进而提高调试效率。

    • 程序编译时以源程序文件为单位而不是以函数为单位进行编译的。

    • C程序的执行是从main函数开始的,如果在main函数中调用其他函数,那么在调用后会返回到main函数中,在main函数中结束整个程序的运行。

    • 所有的函数的平行的,互相独立的,C语言中函数不能嵌套定义,可以互相调用。

    • 函数的定义是指对函数功能的确立,包括指定函数名、函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。

    • 函数的声明的作用是把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。

    1.2

    • 用户角度函数分类
    1. 标准函数:即库函数,由系统提供。
    2. 用户自定义的函数:解决专门需要。
    • 函数的形式分类
    1. 无参函数:一般用来执行指定的一组操作。在调用无参函数时,主调函数不向被调用函数传递数据。

    2. 有参函数:主调函数在调用被调用函数时,通过参数向被调用函数传递数据。

    2.1

    • 函数调用自身的函数称为递归函数,递归函数一定要有结束条件,否则会产生死循环
    • “递归的核心是找到基础公式”
      f ( n ) = n ∗ f ( n − 1 ) f(n)=n*f(n-1) f(n)=nf(n1)

    3.1

    • 全局变量不会因为某个函数执行结束而消失,在整个进程的执行过程中始终有效。
    • 如果局部变量与全局变量重名,那么将采取就近原则,即实际获取和修改的值是局部变量的值。
    • 不建议使用全局变量。
    • 局部变量的有效范围是离自己最近的花括号,括号外无法使用。
    • for循环括号内定义的变量,循环体外不可用。建议将变量定义在循环括号外。
    • 形参可以看成一个局部变量。
    • 关于实参与形参的说明
    1. 定义函数中指定的形参,如果没有函数调用,那么它们并不占用内存中的存储单元。
       
      发生函数调用时,形参分配内存单元。
      在调用结束后,被释放。

    2. 实参可以是常量、变量或表达式,但要求它们有确定的值。
       
      例如,print(i+3)在调用时将实参的值i+3赋给形参。
      print函数可以有多个形参,如 print(int a,int b)

    3. 在被定义的函数中,必须指定形参的类型。
       
      如果实参列表中包含多个实参,那么各参数间用逗号隔开。
      实参与形参的个数应相等,类型匹配,且按顺序对应,一一传递数据。

    4. 实参与形参的类型应相同或赋值应兼容。

    5. 实参向形参的数据传递是单向“值传递”,只能由实参传给形参,而不能由形参传回给实参。
       
      调用函数时,给形参分配存储单元,并将实参对应的值传递给形参;调用结束后,形参单元被释放,实参单元仍保留并维持原值。

    6. 形参相当于局部变量,因此不能再定义局部变量与形参同名,否则会造成编译不通。

    3.2

    • 局部变量
    1. 主函数中定义的变量只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。
    2. 不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。
    3. 形式参数也是局部变量。
    4. 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称“分程序”或“程序块”。上文中的int j=5就是如此,只在离自己最近的花括号内有效,若离开花括号,则在其下面使用该变量会造成编译不通。
    5. 注意一个细节,for循环的小括号内定义的int i,在离开for循环后,是不可以再次使用的。
    • 全局变量
    1. 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。
    2. 使用全局变量过多会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错,因此要有限制地使用全局变量(尽量不用)。
    3. 因为函数在执行时依赖于其所在的外部变量,如果将一个函数移到另一个文件中,那么还要将有关的外部变量及其值一起移过去。然而,如果该外部变量与其他文件的变量同名,那么就会出现问题,即会降低程序的可靠性和通用性。C语言一般要求把程序中的函数做成一个封闭体,除可以通过“实参→形参”的渠道与外界发生联系外,没有其他渠道。
  • 相关阅读:
    抖音矩阵系统,这个排名很难啊。按?
    架构:微服务网关(SIA-Gateway)简介
    flutter 合并数组及操作符
    对于Servlet原理以及Mapping的五种映射和404页面的详解
    安科瑞为工业能效提升行动计划提供EMS解决方案-安科瑞黄安南
    山西佳诺德:短视频有哪些类型
    图像噪声--添加噪声
    【Unity3D】VideoPlayer组件
    day008
    05-树8 File Transfer(并查集)
  • 原文地址:https://blog.csdn.net/m0_58991879/article/details/127724585