函数

作者: 晓晓桑 | 来源:发表于2020-04-14 16:58 被阅读0次

    函数的概念

    模块化编程:

    • 用于完成特定任务的程序代码单元
    • 那就是把一个小功能封装成一个独立的代码段(函数)
    • 封装前和封装后执行的结果一样。
      函数的作用:
    • 增加代码的复用性
    • 增加代码的可读性
    //输出一维数组中最大值
    int getMaxNum(int *a, int length) {
    
        int maxNum = a[0];
        for (int i = 0; i < length; ++i) {
            if (maxNum < a[i]) {
                maxNum = a[i];
            }
        }
    
        return maxNum;
    }
    int main(void) {
    
        int a[5] = {1, 2, 7, 9, 3};
    
        //输出一维数组中最大值
    //    int maxNum = a[0];
    //    for (int i = 0; i < 5; ++i) {
    //        if (maxNum < a[i]) {
    //            maxNum = a[i];
    //        }
    //    }
    //    printf("%d", maxNum);
    
    
        printf("%d", getMaxNum(a, 5));
        return 0;
    }
    
    1. 无参函数:void fun(void)
      参数啥也不写 void fun():表示参数个数不确定


      image.png
    2. C语言中可以不写返回值类型,不写的话是int类型。

    3. 函数名字要符合命名规则:

    • 首字母:只能是大小写字母、下划线(_),不能用数字做首字母。
    • 非首字母:数字、字母、下划线
    • 不能用与系统重复的函数名
    • 函数名字的长度没有限制
    1. 主函数main是操作系统调用的
    2. 函数地址:函数名字就是函数的地址。所以函数调用的时候,本质是:函数地址(参数列表)。
    void fun() {
        printf("11111");
    }
    int main(void) {
    
        //打印函数地址
        printf("%p",fun);//0x100e25f10
    
        return 0;
    }
    

    代码区存代码,方法的名字就是方法的地址,调用方法就是从方法地址的头地址还是调用。fun和&fun都是函数的地址(和int a=12; 不同。a是12,即地址里面的数据,&a是地址 , fun==&fun

    void fun() {
        printf("11111");
    }
    int main(void) {
        (&fun)();//11111
        fun();//11111
        return 0;
    }
    

    C里面,被调用的函数一定放到前面


    image.png

    函数声明

    为了解决函数顺序问题,出现了函数声明。

    1. 在函数外声明
    //函数声明,顺序没要求
    void fun(void);
    void fun2(void);
    
    int main(void) {
        fun();
        fun2();
        return 0;
    }
    void fun(void) {
        printf("11111");
        fun2();
    }
    void fun2(void) {
        printf("2222");
    }
    

    2.在函数内声明

    int main(void) {
        //函数声明
        void fun(void);
        void fun2(void);
        fun();
        fun2();
        return 0;
    }
    
    void fun(void) {
        printf("11111");
        fun2();
    }
    void fun2(void) {
        printf("2222");
    }
    

    注意的是:

    • 函数声明管的范围是他下面的范围,因为程序执行顺序是自上而下的。


      image.png
    • 如果有函数声明,没有写函数定义,调用函数会报错
    • 如果有函数声明,没有写函数定义,没有调用函数,语法无错误,但没有意义。
    • 函数声明可以重复写多个,但是函数定义只能写一个
    //函数声明
    void fun(void);
    void fun2(void);
    
    int main(void) {
        //函数声明
        void fun(void);
        void fun2(void);
        fun();
        fun2();
        return 0;
    }
    //函数声明
    void fun(void);
    void fun2(void);
    void fun(void) {
        printf("11111");
        fun2();
    }
    void fun2(void) {
        printf("2222");
    }
    

    函数类型

    //函数声明
    int fun(void);
    
    int main(void) {
        printf("%d",fun());//会把double类型的4.5直接内存阶段性变成4
        return 0;
    }
    int fun(void) {
        printf("11111\n");
        return 4.9;
    }
    

    结果:

    11111
    4
    

    return

    • 用在有返回值的函数中:作用是终止所在函数的执行,并返回指定的数据。
    • 可以有多个return,但只执行代码逻辑中但第一个
    • 用在无返回值但函数中" return; ":终止函数但执行
    • return一次只能返回一个值
    • 如果函数有返回值,但是没写return,数值类型会返回0。
    //函数声明
    int fun(void);
    
    int main(void) {
        printf("%d",fun());//会把double类型的4.5直接切整数4
        return 0;
    }
    int fun(void) {
        printf("11111\n");
        return 1;
        printf("2222\n");
        return 2;
        return 3;
    }
    

    结果

    11111
    1
    

    return一次只能返回一个值,出现return 2, 3; 其实是一个逗号表达式,逗号表达式结果是最右边这个值。

    //函数声明
    int fun(void);
    
    int main(void) {
        int a = fun();
        printf("%d", a);//结果是3
    
        return 0;
    }
    
    int fun(void) {
        return 2, 3;
    }
    
    返回多个值但技巧,返回一个堆区内存但地址就可以,也就是malloc、calloc的地址。别的像数组什么的栈区地址是不可以的。
    //函数声明
    int *fun(void);
    int main(void) {
        int *p = fun();
        printf("%d,%d", *p,p[1]);//结果是4,5
        free(p);
        return 0;
    }
    int *fun(void) {
        int *p = malloc(8);
        *p = 4;
        p[1] = 5;
        return p;
    }
    
    有参数无返回值

    声明的两种形式:

    1. int fun(int a, int b);
    2. int fun(int , int );
    //函数声明
    int fun(int a, int b);
    int fun2(int, int);
    
    int main(void) {
        printf("%d\n", fun(1, 2));//3
        printf("%d", fun2(1, 2));//3
    }
    int fun(int a, int b) {
        return a + b;
    }
    int fun2(int a, int b) {
        return a - b;
    }
    
    通过函数修改外部变量的值 或者说 把函数内部的值传递到函数外部

    答:通过指针。

    //函数声明
    int fun(int *a);
    
    int main(void) {
    
        int a = 3;
        printf("%d\n", a);
        fun(&a);
        printf("%d\n", a);
    }
    
    int fun(int *a) {
        *a = 12;
        return a;//return 的是a的地址,就是指针a的地址
    }
    
    

    运行结果

    3
    12
    
    通过函数修改指针的指向
    //函数声明
    int fun(int *a);
    
    int main(void) {
    
        int a = 3;
        int *p = &a;
    
        printf("%p\n", p);//0x7ffee8f1e93c
        fun(p);
        printf("%p\n", p);//0x7ffee8f1e93c
    
        int fun(int *a) {
        a = NULL;
    }
    

    上述 fun(p)传的是p的值,即a的地址,所以改变不了指针p的指向,修改成下面的:

    //函数声明
    int fun(int **a);
    
    int main(void) {
    
        int a = 3;
        int *p = &a;
    
        printf("%p\n", p);//0x7ffee8f1e93c
        fun(&p);
        printf("%p\n", p);//0x0 //看,地址被修改了!!
    }
    
    int fun(int **a) {
        *a = NULL; //这里得写*a,这样才能修改a的地址的值
    }
    
    

    综上面三个代码所述:你要修改谁,你就传谁的地址,修改的时候记得加* ,例如a = 12和a = NULL

    通过函数交换两个变量的值

    直接传a,b的值,交换的不是main里面的a,b的值,而是fun里面的地址的值。所以用指针

    //函数声明
    void fun(int *a, int *b);
    int main(void) {
    
        int a = 3;
        int b = 5;
        printf("%d,%d\n", a, b);
        fun(&a, &b);
        printf("%d,%d", a, b);
    }
    void fun(int *a, int *b) {
      int c = *a;
        *a = *b;
        *b = c;
    }
    

    运行结果:

    3,5
    5,3
    

    一维数组做函数参数

    1. 形参是指针
      因为数组的地址是连续的,所以我只有知道数组的首地址,那么就可以遍历数组所有的元素了。
      在函数中,我如果把数组首地址和长度传进去,那就可以操作数组了
      void fun(int *p, int length)
    //函数声明
    void fun(int *a, int length);
    
    int main(void) {
        int a[5] = {1, 2, 3, 4, 5};
        for (int i = 0; i < 5; ++i) {
            printf("%d,", a[i]);
        }
        printf("\n");
    
        //&a[0]==&a
        fun(&a[0], 5);
    
        for (int i = 0; i < 5; ++i) {
            printf("%d,", a[i]);
        }
    }
    
    void fun(int *p, int length) {
        for (int i = 0; i < length; ++i) {
            p[i] = i;
        }
    }
    

    运行结果:

    1,2,3,4,5,
    0,1,2,3,4,
    
    1. 形参是数组
      在C中,数组做形参,他的本质是数组。
    • void fun(int a[], int length) 可以不写数组长度
    • void fun(int a[10000], int length) 这个形参的数组长度多少都没有问题,本质是指针
    //函数声明
    //void fun(int a[], int length);
    void fun(int a[5], int length);
    
    int main(void) {
        int a[5] = {1, 2, 3, 4, 5};
        for (int i = 0; i < 5; ++i) {
            printf("%d,", a[i]);
        }
        printf("\n");
        //&a[0]==&a
        fun(&a[0], 5);
        for (int i = 0; i < 5; ++i) {
            printf("%d,", a[i]);
        }
    }
    
    //void fun(int a[], int length) 可以不写数组长度
    //void fun(int a[10000], int length) 这个形参的数组长度多少都没有问题,本质是指针
    void fun(int  a[5], int length) {
        for (int i = 0; i < length; ++i) {
            a[i] = i;
        }
    }
    
    //函数声明
    //void fun(int a[], int length);
    void fun(int a[10000], int length);
    
    int main(void) {
        int a[5] = {1, 2, 3, 4, 5};
        fun(&a[0], 5);
    
    }
    
    //void fun(int a[], int length) 可以不写数组长度
    //void fun(int a[10000], int length) 这个形参的数组长度多少都没有问题,本质是指针
    void fun(int  a[10000], int length) {
        printf("%d", sizeof(a)); //结果是8,所以a就是个指针。
    }
    
    

    写成数组或者指针没关系,一样,想咋写咋写。
    C中,不能把整个数组传到函数中!!!,只能传一个地址

    二维数组做函数参数

    void fun(int a[2][3], int hang,int lie){}
    void fun(int a[][3], int hang,int lie){}
    void fun(int (*p)[3], int hang,int lie){}

    //函数声明
    //void fun(int a[2][3], int hang,int lie);
    //void fun(int a[][3], int hang,int lie);
    //void fun(int (*p)[3], int hang,int lie)
    void fun(int (*p)[3], int hang,int lie);
    
    int main(void) {
        int a[2][3] = {{1, 2, 3},
                       {4, 5, 6}};
         //二维数组中,a是一维数组指针。
        int (*p)[3] = a;
        fun(p,2,3);
    
    }
    //void fun(int a[2][3], int hang,int lie) //也可以写成这样,跟下面是一模一样的
    //void fun(int a[][3], int hang,int lie) //也可以写成这样,跟下面是一模一样的
    void fun(int (*p)[3], int hang,int lie) {
        for (int i = 0; i <hang ; ++i) {
            for (int j = 0; j <lie ; ++j) {
                printf("%d,",p[i][j]); //1,2,3,4,5,6,
            }
        }
    }
    

    函数地址类型

    函数地址类型取觉于函数的返回值类型、参数类型、个数
    eg:int fun(int a,int b) 这个函数的类似就是int (int a,int b)

    //函数声明
    int fun(int a);
    int main(void) {
    
        //函数指针的变量
        //分析:函数的类型是int (int a),因为fun是指针,所以加个*p
        int (*p)(int a) =fun;// 一样的意思:int (*p)(int a) =&fun;
    
        //用p调用函数
        p(2);
    
    }
    int fun(int a) {
        printf("我是fun");
        return 1;
    }
    

    递归函数

    函数字节调用自己,本质就是循环。
    可循环的三要素:循环控制变量(有初始值)、循环控制变量的变化、循环停止条件。

    递归应用之-用递归打印54321
    //函数声明
    int fun(int a);
    
    int main(void) {
        fun(5);
    }
    
    //用递归打印54321
    int fun(int a) {
        if (a > 0){
            printf("%d\n", a);
            fun(a - 1);
        }
        
        return 1;
    }
    

    递归的本质:

    //函数声明
    int fun(int a);
    int main(void) {
        fun(3);
    }
    
    int fun(int a) {
        if (a > 0){
            printf("前:%d\n", a);
            fun(a - 1);
            printf("后:%d\n", a);
        }
        return 1;
    }
    

    运行结果

    前:3
    前:2
    前:1
    后:1
    后:2
    后:3
    

    上面看出:递归调用自己之前,值是从外到里调用的,调用完之后,再从最后一个值由外向里执行。
    实质是:

    int fun(int a) {//3
        if (a > 0) {
            printf("前:%d\n", a); //打印3
            int fun(int a) {//2
                if (a > 0) {
                    printf("前:%d\n", a);//打印2
                    int fun(int a) {1
                        if (a > 0) {
                            printf("前:%d\n", a);//打印1
                            int fun(int a) {//0 不打印里面的
                                if (a > 0){
                                    printf("前:%d\n", a);
                                    fun(a - 1);
                                    printf("后:%d\n", a);
                                }
                                return 1;
                            }
                            printf("后:%d\n", a);//打印1
                        }
                        return 1;
                    }
                    printf("后:%d\n", a);//打印2
                }
                return 1;
            }
            printf("后:%d\n", a);//打印3
        }
        return 1;
    }
    
    递归应用之-斐波拉契数列

    规则是 第1,2项都是1,第三项之后是,每一项第值都是前两项第和,即:第n项值=第(n-1)项值+第(n-2)项值

    • 求第n项第值:
    //函数声明
    int fun(int a);
    
    int main(void) {
        printf("%d", fun(6));
    }
    //斐波拉契数列,得到第n项第值
    int fun(int n) {
        if (n == 1) {
            return 1;
        } else if (2 == n) {
            return 1;
        } else {
            return fun(n - 1) + fun(n - 2);
        }
    }
    
    

    由上看,由通项公式的技巧:前面写前数列起始值,后面是通项公式。


    image.png
    n!
    //函数声明
    int fun(int a);
    
    int main(void) {
        printf("%d", fun(5));
    }
    
    //n!
    int fun(int n) {
        if (n == 1) {
            return 1;
        } else {
            return n * fun(n - 1);
        }
    }
    
    

    参数不确定的函数

    void fun(int a, ...);
    
    int main(void) {
        fun(3,1, 1.3, 4);
    }
    
    //参数不确定的情况,第一个参数一定要写,a的意思是后面要传a个参数
    void fun(int a, ...) {
        va_list b;//定义参数数组
        va_start(b, a);//把参数装进数组
        int v1 = va_arg(b, int);//取第一个值,因为第一个值是1,int型
        float v2 = va_arg(b, double);//取第二个值,因为第一个值是1.3,double型
        int v3 = va_arg(b, int);//取第一个值,因为第三个值是4,int型
        printf("%d,%lf,%d",v1,v2,v3);
    }
    
    
    [ ]的作用
    • 声明变量的时候有[],表示声明的是数组变量
    • 函数有[],表示指针
    • 地址+[],表示下标运算

    相关文章

      网友评论

          本文标题:函数

          本文链接:https://www.haomeiwen.com/subject/zyxaphtx.html