C语言:函数

39 阅读7分钟

C语言:函数

1.引言:

在C语言编程中,函数是不可或缺的,有了函数,所以我们的编程变得更加简便,不需要像过去的人一样,每写一次代码都需要临时写一大段代码达到打印的目的,过去的人们意识到这大幅度影响到了写代码的效率,所以,便有了今天经典的库函数,也渐渐为编程引入了函数的概念

2.函数的种类

在C语言中,函数分为了两大种:

  • 库函数
  • 自定义函数

库函数,顾名思义,就是引用了库中的函数

而自定义函数也很好理解,就是自己临时写了一个函数,后面想用这个函数便可以直接引用,而不需要引用库中的函数,这也奠定了其在编程中的重要地位

3.库函数

在库函数使用前,我们必须得做一件事情,那便是引用其相应的头文件,就像最经典的

#include<stdio.h>

这就是输入输出的标准函数:standard input output

在我们写代码的过程,其几乎是必不可少的一部分,没有了它,我们连基本的输入输出都很难做到,因为这需要我们临时写一段输入输出的代码

假如不引用编译器就会报错:

未定义标识符XXXX

这就相当于一个路人连工具箱都没见过,就让他直接去使用工具箱内的道具,这显然不合理,所以至少得让他摸到工具箱,然后才能使用其中的工具

这就是我们在使用库函数时,必须得先引用其相应的头文件,这就能使编译器能辨别出你的想法,才能完成你给予编译器的任务

除此之外还有

#include<string.h>
#include<Windows.h>
#include<math.h>
......

有兴趣的可以去像MSDN之类的网站查一查有哪些库函数,和其包括的头文件,这里就不再一一赘述了

4.自定义函数

自定义函数在我们日常编译中发挥着巨大作用,它可以提高我们的写代码的效率,例如:当我们在反复需要一个功能时,便可以通过写一个函数来实现这功能,当下面还需要用到该功能时,直接引用函数即可,不需要又重新的去写这一段代码

例如:

这里需要我们通过一个函数来实现判断某一年是否为闰年

#include <stdio.h>void leapYear(int x)
{
    if((x%4==0&&x%100!=0)||(x%400==0))
    {
        printf("%d是闰年\n",x);
    }
}
​
int main()
{
    int year=0;
    scanf("%d",&year);
    leapYear(year);
    return 0;
}

或是通过一个函数来判断一个数是否为质数

#include <stdio.h>
#include <math.h>int isPrime(int x)
{
    if (x == 1)
    {
        return 0;
    }
    if (x == 2||x==3)
    {
        return 1;
    }
    for (int i = 2; i <= sqrt(x); i++)
    {
        if (x % i == 0)
        {
            return 0;
        }
    }
    return 1;
}
​
int main()
{
    int num = 0;
    scanf("%d", &num);
    if (isPrime(num))
    {
        printf("%d是质数\n",num);
        return 0;
    }
    else
        printf("%d不是质数\n",num);
    return 0;
}

5.函数的嵌套

函数在使用中存在着嵌套使用

即:

#include <stdio.h>
#include <math.h>int isPrime(int x)
{
    if (x == 1)
    {
        return 0;
    }
    if (x == 2||x==3)
    {
        return 1;
    }
    for (int i = 2; i <= sqrt(x); i++)
    {
        if (x % i == 0)
        {
            return 0;
        }
    }
    return 1;
}
​
void leapYear(int x)
{
    if(isPrime(x))//判断一年是否为质数和闰年
    {
        if((x%4==0&&x%100!=0)||(x%400==0))
    {
        printf("%d是闰年\n",x);
    }
    }
}
​
​

上面的例子就是一个简单的函数嵌套,但是函数不能嵌套定义,例如:

#include <stdio.h>
#include <math.h>int isPrime(int x)
{
    void leapYear(int y)
{
    if((y%4==0&&y%100!=0)||(y%400==0))
    {
        printf("%d是闰年\n",y);
    }
}
​
    if (x == 1)
    {
        return 0;
    }
    if (x == 2||x==3)
    {
        return 1;
    }
    for (int i = 2; i <= sqrt(x); i++)
    {
        if (x % i == 0)
        {
            return 0;
        }
    }
    return 1;
}

上面这种就是所谓的嵌套定义,但这是一种错误的写法,函数是不允许嵌套定义的

6.函数的链式访问

函数还可以进行链式访问,例如:

#include <stdio.h>int main()
{
    printf("%d",printf("%d",return 34);
    return 0;
}

这就是一个经典的链式访问,打印结果是:1234,至于为什么?

原因是printf打印的是return返回的字符数量,所以第2个printf输出的是2,因为第2个printf打印的字符是1个,所以第1个printf打印的是1

因此最终结果是1234

7.函数的声明

对于函数的使用通常需要进行声明

例如:

int isPrime(int x);//这就是函数的声明
​
int isPrime(int x)
{
    if (x == 1)
    {
        return 0;
    }
    if (x == 2||x==3)
    {
        return 1;
    }
    for (int i = 2; i <= sqrt(x); i++)
    {
        if (x % i == 0)
        {
            return 0;
        }
    }
    return 1;
}

当然正常的函数声明会放在头文件中

就像

屏幕截图 2024-10-07 001521.png

这就是将声明放在头文件中,之后想便可以直接引用源文件中的isPrime函数了

8.函数的递归与迭代

函数的递归

函数的递归顾名思义就是将一个函数有放进本身的这个函数,简单来说就是重复使用一个函数直到满足限制条件

例如:

输入一个字符串,并模拟strlen将其长度打印出来

#include <stdio.h>

int Count(char* x)
{
	if (*x != '\0')
		return 1 + Count(x + 1);
	else
		return 0;
}

int main()
{
	char arr[] = "abcdef";
	int count = Count(arr);
	printf("%d\n", count);
	return 0;
}

这个例子中就可以使用了递归

在此我得先传递一下递归思想:递归思想就是将一件麻烦的事,逐渐逐渐地转化为一件重复的简单事情,像这里我们知道当字符串遇到\0时就会停止,所以我们可以将第1个字符看作是1,并将后面所有的部分看作是(x),以此类推,直到碰见\0,然后结束整个过程

再看一个例子:

输入一串数字,并将其依次打印出来(而非一次性打印出来)

#include <stdio.h>

void read(int x)
{
	if (x > 9)//大于9说明还不是个位数,也同时增添了限制条件
	{
		read(x / 10);//可以每次减少末尾的1个数字,也同时在接近限制条件
	}
	printf("%d	", x%10);//x%10可以得到个位数字
}

int main()
{
	int x = 0;
	scanf("%d", &x);
	read(x);
	return 0;
}

这个例子就很好地解释了何为递归

从上面的例子中我们都看见了使用递归时必须要有限制条件,且随着递归的进行,离限制条件越来越近,假如缺少其中1个那都形成了死递归,最终导致程序崩溃

在此我还得补充一点,那就是当使用函数时,函数中会存在形参,而形参就是实参的一份临时拷贝,每当使用一次函数,形参会向内存中的栈区申请一定内存,而内存总是有限的,因为死递归,导致形参一直向栈区申请内存,当形参申请的内存过多时,就会出现栈溢出的现象,也是导致程序崩溃的原因,这就是为什么需要上面的限制条件和随着递归的进行,离限制条件越来越近

函数的迭代

函数的迭代就是函数中的值随着循环逐渐改变,它在有些地方可以使函数的效率加快

例如:

写出斐波那契数列

#include <stdio.h>

int Fib(int x)
{
	if (x < 3)
	{
		return 1;
	}
	else
	{
		return Fib(x - 1) + Fib(x - 2);
	}
}

int main()
{
	int num = 0;
	scanf("%d", &num);
	int ret = Fib(num);
	printf("%d\n", ret);
	return 0;
}

通过递归的形式,我们发现也可以做到,但随着数字输入的增加,我们发现,其打印的效率肉眼可见的在降低,通过调试发现,每当其返回一次,它就会向前重新计算一次,而不会将固有值记录下来,这就是其效率低的主要原因

这时候就可以使用迭代,因为迭代会记录下每一次运行的值,在这方面,可以提高函数的效率

#include <stdio.h>

int Fib(int x)
{
	int x1 = 1;
	int x2 = 1;
	int tmp = 1;

	while (x > 2)
	{
		tmp = x1 + x2;
		x1 = x2;
		x2 = tmp;
		x--;
	}

	return tmp;
}

int main()
{
	int num = 0;
	scanf("%d", &num);
	int ret = Fib(num);
	printf("%d\n", ret);
	return 0;
}

最终通过几次测试,随着输入的数字增大,其效率远远超过使用递归的方法

总结

函数在我们的编程中起着至关重要的作用,特别是自定义函数,以及函数的递归与迭代