zoukankan      html  css  js  c++  java
  • 函数

    C语言函数传参

    C 语言中,函数的参数传递方式有两种:传值调用和传址调用。

    传值调用

    将实参的值复制到形参相应的存储单元中,即形参和实参分别占用不同的存储单元,这种传递方式称为“参数的值传递”或者“函数的传值调用”。

    值传递的特点是单向传递,即主调函数调用时给形参分配存储单元,把实参的值传递给形参,在调用结束后,形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值,实参的存储单元仍保留并维持数值不变。

    传址调用

    这种方式使用数组名或者指针作为函数参数,传递的是该数组的首地址或指针的值,而形参接收到的是地址,即指向实参的存储单元,形参和实参占用相同的存储单元,这种传递方式称为“参数的地址传递”。

    地址传递的特点是形参并不存在存储空间,编译系统不为形参数组分配内存。数组名或指针就是一组连续空间的首地址。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该首地址之后,与实参共同拥有一段内存空间,形参的变化也就是实参的变化。

    递归函数

    递归的基本原理

    递归函数即自调用函数,在函数内部直接或间接地自己调用自己,即函数的嵌套调用是函数本身。

    • 每一级的函数调用都有自己的变量。
    • 每一次函数调用都会有一次返回。
    • 递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序。
    • 递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。
    • 虽然每一级递归都有自己的变量,但是函数代码并不会得到复制。
    • 递归函数中必须包含可以终止递归调用的语句。

    递归的优缺点

    其优点在于为某些变成问题提供了最简单的解决方法,简化了程序设计。

    缺点是一些递归算法会很快耗尽计算机的内存资源。同时,使用递归的程序难于阅读和维护。

    基本递归

    例如,递归方式计算阶乘:

    int fact(int n) {
        if(n < 0)
            return 0;
        else if (n == 0 || n == 1)
            return 1;
        else
            return n * fact(n - 1);
    }
    

    以计算4!为例:

    归方法中的需要的所有状态通过方法的参数传入下一次调用中。

    • 尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。

    尾递归实现计算阶乘:

    int facttail(int n, int a)
    {
        if (n < 0)
            return 0;
        else if (n == 0)
            return 1;
        else if (n == 1)
            return a;
        else
            return facttail(n - 1, n * a);
    }
    

    可变参数列表

    可变参数列表可通过宏来实现,这些宏定义在stdarg.h头文件中,它是标准库的一部分。这个头文件声明了一个类型va_list和三个宏:va_startva_argva_end。可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数的值。

    参数列表的可变部分位于一个或多个普通参数(命名参数)的后面(即参数列表中至少要有一个命名参数),它在函数原型中以一个省略号表示。

    例如求平均数:

    #include <stdarg.h>
    
    float average(int n_values, ...)
    {
    	va_list var_argue;
    	int count;
    	float sum = 0;
    
    	va_start(var_argue, n_values);
    
    	for (count = 0; count<n_values; count++)
    	{
    		sum += va_arg(var_argue, int);
    	}
    
    	va_end(var_argue);
    	return sum / n_values;
    }
    
    • 函数声明一个名叫var_argue的变量,它用来访问参数列表的未确定部分。
    • var_argue通过调用va_start来初始化。它的第一个参数是va_list 类型变量的名字,第二个参数是省略号前最后一个有名字的参数。初始化过程把变量var_argue设置为指向可变参数部分的第一个参数。
    • 为了访问参数,需要使用va_arg,这个宏接受两个参数:va_list类型变量和参数列表中下一个参数的类型。在这个例子中,所有的可变参数都是整型。va_arg返回这个参数的值,并使var_arg指向下一个可变参数。
    • 当访问完毕最后一个可变参数之后,需要调用va_end

    可变参使用注意事项

    • 参数列表中,至少有一个命名参数,如果连一个命名参数都没有,就无法使用arg_start宏。

    • 可变参数的类型和个数完全由程序代码控制,它并不能智能地识别不同参数的个数和类型。如果在va_arg中指明了错误的类型,其结果是不可预知的。

    • 可变参数只能从头到尾逐个访问,如果你在访问几个参数后想中途退出,这是允许的,但是直接访问参数列表的中间部分是不行的。

  • 相关阅读:
    C++语法小记---string类
    C++语法小记---标准库
    C++语法小记---运算符重载
    C++语法小记---函数重载
    C++语法小记---友元
    C++语法小记---开篇
    STM32使用printf丢失第一个字母的问题
    AD芯片的基准参考电压问题
    运算放大器的调试经验
    [置顶] TIM_GetCounter与TIM_GetCapture1的区别
  • 原文地址:https://www.cnblogs.com/chay/p/11521340.html
Copyright © 2011-2022 走看看