C语言———函数(新定义理解递归)

函数的定义

        在计算机科学中,子程序是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。

一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软 件库。

函数的分类

库函数

        在C语言建立的初期,各个公司都创建了属于自己的一套常规使用的函数,为了能够统一常规使用的函数,提高工作效率和代码的可移植度,C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。

        我们可以通过下面这些网站查找自己想要的库函数:

        www.cplusplus.com

        http://zh.cppreference.com

自定义函数

        自定义函数包括以下几部分,返回值,函数参数,函数名,与主体。

        将main函数中的内容传递给函数后,函数参数进行函数内容的代码运行,最终返回或改变参数的内容。函数可以明细操作内容,更好地分装化工作项目,使得代码的可读性提高。

int get_max(int x, int y) 
{
    return (x>y)?(x):(y);
}
int main() 
{
    int num1 = 10;
    int num2 = 20;
    int max = get_max(num1, num2);
    printf("max = %d\n", max);
    return 0;
}

        这个函数将num1和num2的数值传递给get_max这个函数中的x和y,通过x与y的相互运算,得到较大数并返回。 

void Swap1(int x, int y) {
    int tmp = 0;
    tmp = x;
    x = y;
    y = tmp;
}

int main() 
{
    int num1 = 1;
    int num2 = 2;
    Swap1(num1, num2);
    printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
    return 0;
}

         当我们调用上面这个函数,想要交换num1和num2的值的时候,我们发现最终得到的结果并不是我们想要的,这两个数压根没有交换。这是因为函数在栈上开辟空间,创建了x,y,tmp这些值,函数运行完毕后x和y的值确实交换了,但也销毁了,函数中的变量改变并没有影响函数外num1和num2的值,这就不得不提到一下的概念。

函数的参数

实际参数(实参)

        真实传给函数的参数,叫实参。 实参可以是:常量、变量、表达式、函数等。

        无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

形式参数   (形参)

        形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。

        形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有 效。

        因此我们可以分析出,上面 Swap1 和 Swap2 函数中的参数 x,y,px,py 都是形式参数。在main函数中传给 Swap1 的 num1 , num2 和传给 Swap2 函数的 &num1 , &num2 是实际参数。

        实际上,在Swap1 函数在调用的时候, x ,y 拥有自己的空间,同时拥有了和实参一模一样的内容。 所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝。

函数的调用

传值调用

        函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。

传址调用

        传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

        这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操 作函数外部的变量。

        因此,我们可以对swap函数从传值调用改为传址调用,这样的话就可以通过函数内部的x,y对函数外部的num1,num2进行改变。

void Swap(int *px, int *py) {
    int tmp = 0;
    tmp = *px;
    *px = *py;
    *py = tmp;
}
Swap(&x,&y);

        在函数的调用过程中,我们前面要加上&,因为这个时候要传的是x和y的地址。

嵌套调用

#include <stdio.h>
void new_line()
{
    printf("hello world\n");
}
void three_line()
{
    int i = 0;
    for(i=0; i<3; i++)
    {
        new_line();
    }
}
int main() 
{
    three_line();
    return 0; 
}

        即在一个函数中调用另外一个函数,需要注意的是,可以嵌套调用,但是不能嵌套定义函数。

链式访问

#include <stdio.h>
int main() 
{
    printf("%d", printf("%d", printf("%d", 43))); 
    //printf函数的返回值是打印在屏幕上字符的个数 
    return 0;
}

        即把一个函数的返回值做参数在另一个函数中进行调用。

函数的递归

        即程序自身调用自己,和循环一样,我们需要一个限制条件来停止递归,在每次递归过程中,需要逼近这个限制条件。

        

#include <stdio.h>
void print(int n)
{
    if(n>9) 
    {
        print(n/10);
    }
    printf("%d ", n%10);
}
int main() 
{
    int num = 1234;
    print(num);
    return 0;
}

        递归的过程是这样的,首先num = 1234,num/10 = 123,进入递归,/10 = 12,进入递归,/10 = 1,不满足>9的条件执行打印,屏幕上打印1 ,然后此次递归代码全部运行完毕,返回至上一次递归,即12,再打印2 ,然后再返回上次递归,打印3,然后返回上一次递归,打印4,最后所有递归都返回回来了,函数执行完毕,屏幕显示1 2 3 4 。

        递归相当于梦中梦,当你做梦一直往下做,做的越来越深,最后也要一层层的醒来,把每一次醒来后的事情做完后,返回上一次梦境。

        用递归完成题目时,需要注意,我们要研究问题的子问题,并且研究清楚递归的结束条件,比如计算n的阶乘,我们可以想成n! = n * (n - 1)!

int factorial(int n)
{
    if(n <= 1)
        return 1;
    else
        return n * factorial(n-1);
}

        当我们遇到一些问题,无法用递归解决或者用递归解决的效率极低时,可以考虑用迭代来解决问题。

        迭代就是重复反馈过程的活动。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。此过程的每一次结果,都是由对前一次所得结果施行相同的运算步骤得到的。

上一篇:Java方法


下一篇:flowable 控制台打印出自带表的 sql 语句