函数

2020-04-14  本文已影响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");
}

注意的是:

//函数声明
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

//函数声明
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[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)项值

//函数声明
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);
}

[ ]的作用
上一篇下一篇

猜你喜欢

热点阅读