Bootstrap

C语言函数详解大全

一.函数是什么

数学中我们对函数就已经有了大致的了解。同理,C语言中也是有函数的,在计算机界中,函数被称作为子程序。

子程序:是一个大型程序中的某部分代码,有一个或多个语句块组成,负责完成某项特定任务,相较于其他代码,具备相对的独立性。

函数中一般会有输入参数并有返回值,提供对过程的封装和细节。

二.C语言函数的分类

C语言中函数可分为:

                                库函数

                                自定义函数

库函数

什么是库函数?

        C语言中常用的功能,用来进行封装,封装成一个个函数,提供出来大家都可以使用。、

        例如:scanf函数(用于输入)        printf函数(用于打印)        strlen函数(用于求字符串长度)等等

更好的了解更多库函数:

cplusplus.com - The C++ Resources Network

常见的库函数:

                        IO函数---->输入输出函数(scanf    printf    getchar    putchar 等)

                        字符串操作函数---->对字符串进行操作(strlen    strcmp    strcpy等)

                        时间/日期操作函数---->time等

                        数学函数----> 对一些相关的数学运算(sqrt    pow等) 

注意:使用库函数前必须得包含相应的头文件 #include

自定义函数

如果库函数能干所有的事情,那还要程序员干什么?
所以这就引出 自定义函数这一相关概念。
自定义函数 :与库函数一样,有函数名,函数参数,返回类型。不过相较于库函数,自定义函数就需要我们自己来设计,根据自己的需求设计出相关的函数。这就给了程序员很大的发挥空间。

函数组成:

ret_type funname(paral1,paral2)
{
    statement;    // 语句项
}

ret_type -> 返回类型
funname -> 函数名
paral1/paral2 -> 函数参数

举个小梨子:

写一个函数求出两个数的和。

#include<stdio.h>

// get_sum的设计
int get_sum(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int sum = get_sum(a, b);
	printf("%d", sum);
	return 0;
}

 分析梨子:

get_sum函数的两个参数 a和b 必须得与实现get_sum的两个参数 x和y 类型相应,并且返回类型也应相对应。

三. 函数参数

函数参数共分为实际参数和形式参数

实际参数(实参):

                真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

形式参数(形参):

                形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁。因此形式参数只在函数中有效。

例如:写一个函数来求两个数的和

#include<stdio.h>

int SUM(int x, int y)
{
	return x + y;
}

int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int s = SUM(a, b);
	printf("%d", s);
	return 0;
}

上述程序中 swap1 和 swap2 函数中的参数 x,y,pa,pb都是形式参数,在 main 函数中传给swap1的a,b和传给swap2的&a,&b都是实际参数。

形参实例化之后其实相当于实参的一份临时拷贝。

四. 函数的调用

传值调用

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

形参是实参的一份临时拷贝,对形参的修改不会影响实参。

比如:写一个程序比较两个数的较大值

#include<stdio.h>
int MAX(int x, int y)
{
	return (x>y)?x:y;
}

int main()
{
	int num1 = 0;
	int num2 = 0;
	scanf("%d %d", &num1, &num2);
	int m = MAX(num1, num2);
	printf("%d", m);
	return 0;
}

 在这一程序中,只是使用了num1 和num2 的值来操作,通过传参两个值来得出最终的结果。

传址调用

传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式,这种方式可通过形参的指针能够访问到函数外部的变量并进行操作。

例如:写一个函数来交换两个整形变量的内容

#include<stdio.h>

void swap1(int* pa, int* pb)
{
	int tmp = 0;
	tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	
	swap1(&a, &b);
	printf("a = %d, b = %d", a, b);
	return 0;
}

在这一程序中,我们主要的问题就是要改变a 和 b 的值,并不需要返回一个值,这就需要使用传址调用。 

五. 函数的嵌套调用和链式访问

嵌套调用

        函数与函数之间可以根据实际的需求进行结合,即相互调用。通俗的来讲就是函数里面套函数。

#include<stdio.h>

void test1()
{
	printf("hello\n");
}

void test()
{
	test1();
	printf("hehe");
}

int main()
{
	test();
	return 0;
}

这一程序就是一个函数的嵌套调用:main函数里套test 函数,test函数里套test1函数。 

链式访问

        把一个函数的返回值作为另一个函数的参数。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "hello";
	printf("%d", strlen(arr));
	return 0;
}

将strlen 函数的返回值作为 printf 函数的参数,即将两个函数通过链条一样连接起来,这就是链式访问。

链式访问相关例题:

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

 分析之前,先通过库函数查询网站将printf 函数格式查找出来

这段程序代码最终结果是:3521

为什么呢?

通过查询发现:printf这个函数的返回值是它打印字符的个数 

若要执行printf 函数1,则就得执行 printf 函数2, 若要执行printf 函数2,则就得执行 printf 函数3,

故此:函数执行顺序:printf 3 -->printf 2 --> printf 1

printf 3函数的打印结果是:35

35是两个字符:所以->第二个printf函数: printf("%d",2) --> 打印结果是:2

2是一个字符:所以->第一个printf函数: printf("%d",1) --> 打印结果是:1

所以打印结果就是:3521

六. 函数的声明和定义

函数的声明

        1.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。

        2.函数的声明一般出现在函数的使用之前。要满足先声明后使用。

        3.函数的声明一般要放在头文件中的。

函数的定义

        函数的定义指函数的具体实现,交代函数的功能实现。

C语言中为什么会既有函数的声明,又有函数的定义?

这就引用了一个新概念:函数分模块化

一个项目通常会分成三部分来写:

函数声明:---> test.h

函数实现:---> test.c

主程序:---> main.c(3-27.c)

通常库函数都会放在 .h文件中 

具体应用:可查看三子棋的实现:-->   写文章-CSDN创作中心

七. 函数递归

递归的定义:

        程序调用自身的编程技巧称为递归(recursion)。做为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。

        递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

        递归的主要思考方式在于:把大事化小

递归的两个必要条件:

        1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。

        2.每次递归调用之后越来越接近这个限制条件。

递归与迭代

        通俗的讲,迭代就是循环的一种,也就是,迭代就是一种循环

为什么会有递归与迭代呢?

        递归虽然使得代码简洁,使得少量的代码就能解决大量的问题,但是递归的大量重复严重影响了机器的运行速度,并且,函数的调用是在栈区进行的,大量的递归可能使得栈溢出,即栈区空间被用完。

        进行迭代时,程序不需要调用大量函数,重复计算的次数就会要少很多,程序的运行速度也会加快,但是程序代码量就会变多。

比如:求斐波那契数列的第 n 项

斐波那契数列:1 1 2 3 5 8 13...(一个数等于前两项相加)

递归程序:

#include<stdio.h>

int fib(int a)
{
	if (a <= 2)
		return 1;
	return fib(a - 1) + fib(a - 2);
}

int main()
{
	int m = 0;
	scanf("%d", &m);
	int ret = fib(m);
	printf("%d", ret);
	return 0;
}

迭代程序:

#include<stdio.h>

int fib(int a)
{
	int sum = 0;
	int i = a;
	int x = 1;
	int y = 1;
	while (i > 2)
	{
		sum = x + y;
		x = y;
		y = sum;
		i--;
	}
	if (i <= 2)
		return sum;
}

int main()
{
	int m = 0;
	scanf("%d", &m);
	int ret = fib(m);
	printf("%d", ret);
	return 0;
}

;