C语言函数的那些子事儿

1、初级的函数设定及调用输出

(1)函数的声明、调用与设定

在这里插入图片描述
以上图片中,5、4行为函数的声明,意思是“ 我有这些个函数 ”,void后面那些个字母,是我给这些个函数起的名字

7、8、9行为函数的调用,意思是“ 我要用这些个函数啦!”

11、16行则为函数的设定,意思是“ 我为有的这些个函数下个定义”

(2)这里面涉及三目运算,可简化设计,它等同于下面注释的 if 语句

在这里插入图片描述
注意我们所设定的函数声明里的参数是形式参数,在调用的时候用的是实际参数,他有一个传参的过程

此处用到三目运算符【x>y?x:y】意思是x如果大于y则输出x的值,若不大于,则输出y的值。

通俗的表达式为k ? m : n,先计算条件k,然后进行判断。如果k的值为真,计算m的值,运算结果为m的值;否则,计算n的值,运算结果为n的值。

一个条件表达式绝不会既计算m,又计算n。条件运算符是右结合的,也就是说,从右向左分组计算。例如,a ? b : c ? d : e将按a ? b : (c ? d : e)执行。

2、返回值类型与函数类型不同会发生什么

在这里插入图片描述
float是浮点型数据类型,它是有小数点的,精度要比整数型数据类型(没有小数点)高

所以上题虽然a,b是float型,但在计算后的返回值给了int型的c,因此在输出时精度降低了,木有小数点后面的东东了,你看下面,这不就好了吗

在这里插入图片描述

3、函数的调用方式

有自左向右,也有自右向左
以下面这个为例,先不看输出
若自左至右,则函数调用相当于 f (2,3)
若自右向左,则函数调用相当于 f (3,3)
然后就可以理解输出结果了
在这里插入图片描述

4、函数声明与调用位置

这两张图上的操作可以看出:
当声明在前面,中间已经调用函数,则需要在后面定义函数
当函数定义已经放到前面,则后面在调用函数之前不加声明也可以
在这里插入图片描述
在这里插入图片描述

5、递归调用函数(头递归)(尾递归)

在这里插入图片描述
在这里插入图片描述
头递归的实现中,在进行下一层的调用前,没有进行计算。在下一层返回,才完成了这一层的计算。

尾递归的实现中,在进行下一层的调用前,会先进行计算,而在最终一般条件满足时,会将计算的结果逐层直接返回。

6、判断对错

1、自己定义的函数只可以在main函数中被调用(错)
自己定义的函数可以在其他的数中被调用
2、C语言中定义的函数可以没有返回值(对)
void类型的函数都是没有返回值的
3、尾递归方式调用函数时,往往会传递一个“积累”作用的参数(对)
4、递归方式解决问题效率一定比先得环解决同一问题更高。(错)
递归解法很多时候都不如环解法效率高。
5、在一个递归调用的数中,不可以嵌套调用别的非自身的的数(错)
被递归调用的函数中可以包括其他的函数,比如在递归返回值内使用数学函数
6、c语言的函数定义时可以不写返回值关型(错)
函数是需要有返回值类型的,如果不返回也需要写void类型
7、C语言中定义的函数可以没有参数(对)

7、变参函数——va族

可变参数函数就是输入的参数的个数是可变的

获得a往后的参数列表——va_list类型的变量,表示可变参数列表类型,实际上就是一个char指针
定位a后面第一个参数的位置——va_ start函数,用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
获取下一个可变参数列表中的参数——va_arg函数,用于获取当前a所指的可变参数并将a指针移向下一可变参数
结束整个获取可变参数列表的动作——va_end函数,用于结束对可变参数的处理

对可变参数列表的处理过程一般为:
1、用va_list定义一个可变参数列表
2、用va_start获取函数可变参数列表
3、用va_arg循环处理可变参数列表中的各个可变参数
4、用va_end结束对可变参数列表的处理

#include <stdio.h>
#include <inttypes.h>         //宏:INT32_MIN所在的头文件
#include <stdarg.h>           //va一族所在的头文件

int max_int(int n, ...)       //变参列表...中的前n个值中返回一个最大值。省略号(...)表示任意个数的参数
{ 
    int ans = INT32_MIN;      //宏:INT32_MIN表示32位整形极小值
    va_list arg;              //通过va_list定义一个变量arg
    va_start(arg, n);         //通过va_start给arg赋值,将变量n后面的变参列表的值赋给arg
    while (n--) 
    {
        int temp = va_arg(arg, int);        //从arg中获取一个int类型的值
        if (temp > ans) 
        {
            ans = temp;
        }
    }
    va_end(arg);
    return ans;
}

int main() 
{
    printf("%d\n", max_int(3, 1, 5, 10));                  //输出为10
    printf("%d\n", max_int(2, 1, 3));                      //输出为3
    printf("%d\n", max_int(6, 1, 3, 5, 7, 13, 15, 17));    //输出为15,因为第一个参数为6,运行时只获取了第一个参数后面的六个数进行比较运算
    return 0;
}

8、函数地址做函数参数

c语言中函数和变量类似,也是有自己的内存地址的。但是,函数不像变量—样可以进行值传递,在将其作为函数参数进行传递时,需要传递它的地址。

对于上面这种用情况,函数g 需要有一个形式参数用于接收函数地址。如下面所示形式的函数,在这个函数定义中,第一个参数需要─个返回值类型为float且有一个int类型参数的函数,第二个参数就是普通的int类型的值。

int g(float (*f)(int), int a) 
{
    return f(a) ;
}

可以看到,函数g 的第一个形式参数f 接收了一个函数的地址,而在C语言中,直接写f(a)其实就可以通过这个地址使用这个函数,并且给它传入一个参数a。这与用取值符取出函数地址并进行函数调用(*f)(a)的方式是等价的。

不同于传变量地址时需要用取值符的做法,在调用上面这个g 函数,并且将函数地址作为参数时,直接将函数名传入就可以了。如果希望将

float sqrt_minus_one(int x) 
{
    return sqrt(x)- 1;
}

作为参数,和变量number一起传入给g,那么,调用时则应该写:

g( sqrt_minus_one,number);

将sqrt_minus_one函数和变量number传入到了g后,形式参数float (*f)(int)接收了sqrt_minus_one函数的地址,形式参数int a接收了number的值。接下来,通过f(a)的方式,传入的函数sqrt_minus_one被调用,并将a作为参数传入到了sqrt_minus_one函数中。sqrt_minus_one函数接收到了值后进行了计算并返回,在g函数中,计算结果返回。至此,最初调用的位置得到了计算的最终结果。

举个栗子:牛顿法求方程近似解

#include <stdio.h>
#include <math.h>
#define EPSILON 1e-6

double f(double x) 
{
    return 2 * pow(x, 3) - 4 * pow(x, 2) + 3 * x - 6;
}

double f_prime(double x) 
{
    return 6 * pow(x, 2) - 8 * x + 3;
}

double h(double x) 
{
    return pow(x, 3) - 4 * pow(x, 2) + 3 * x - 6;
}

double h_prime(double x) 
{
    return 3* pow(x, 2) - 8 * x + 3;
}

double newton(double (*fp)(double),double (*f_prime)(double)) 
{
    double x = 1.5;
    
    while (fabs(fp(x)) > EPSILON)
    {
        x = x - fp(x) / f_prime(x);
    }
    return x;
}

int main() 
{
    
    printf("%g\n", newton(f,f_prime));
    printf("%g\n", newton(h,h_prime));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值