1.函数是什么

函数:子程序:由语句块组成 一般有输入参数和返回值。(也就是说:你可以用我 但我的作用就只是那些固定的、特殊的作用)

有库函数和自定义函数两种类型

2.库函数

由系统提供的函数 能经常使用的函数。我们在开发的过程中每个程序员都可能用的到为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。

类型:IO函数;字符串操作函数;字符操作函数;内存操作函数;时间/日期操作函数;数学函数;其他函数

库函数:​​www.cplusplus.com;www.cppreference.com​

3.自定义函数

自定义函数和库函数一样,有函数名 返回值类型和函数参数。这给程序员一个很大的发挥空间,但是不一样的是这些都是我们自己来设置。

如:

ret_type fun_name(para1,*)
{
statement;//语句项/函数体--交代的是函数的实现
}
//ret_type返回类型
//fun_name自定义的函数名
//para1函数参数

注:如果所命名的函数没有返回值则用void

4.函数的参数

实际参数(实参)

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

形式参数(形参)

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

注:当实参传给形参的时候,形参其实是实参的一份临时拷贝,对形参的修改是不会改变实参的(也就是说:对名字的修改是不会改变你本人的性格的)

5.函数的调用

传值调用

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

(相当于:需要返回一个值)

(例子可查看我的文章:“​​打印100到200的素数(函数)​​​”“​​打印1000-2000的闰年(函数)​​​”“​​实现一个整型有序数组的二分查找(函数)​​”)

传址调用

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

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

(相当于:把别人家偷了 家是别人的了 数值不改也不行)(连根拔起)

(例子可查看我的文章:“​​打印两个整数并交换位置(函数)​​​””​​写一个函数 每次调用一次这个函数,就会将num的值增加1​​“)

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

函数和函数之间可以有机的组成。

嵌套调用

例子如下:

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

主要部分:

void three_line()
{
int i=0;
for(i=0;i<3;i++)
{
new_line();
}
}

链式访问

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

举例1:

#include<stdio.h>
#include<string.h>
int main()
{
int len=0;
len=strlen("abc");
printf("%d\n",len);
printf("%d\n",strlen("abc"));
return 0;
}

也就是说这里的

int len=0;
len=strlen("abc");
printf("%d\n",len);

printf("%d\n",strlen("abc"))

实际上是一个效果

举例2:

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

函数_#include

原因是因为printf的返回值是整数 而且!返回值打印的是字符的个数

也就是说从最内层开始 

第一次printf打印43,43占两个字符,打印2

第二次printf打印2,2占1个字符,打印1

所以输出”4321“

7.函数的声明和定义

函数声明

1.告诉编译器有一个函数叫什么,参数是什么,返回值类型是什么。但是具体是不是存在,无关紧要。(因为不知道是否有过定义等问题)

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

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

函数定义

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

举例:

#include<stdio.h>
int Add(int x,int y);
//函数的声明
int main()
{
int a=10;
int b=20;
int sum=Add(a,b);
printf("%d \n",sum);
return 0;
}
//函数的调用
int Add(int x,int y)
{
int z=x+y;
return z;
}
//函数的定义

(此代码没什么实际意义 仅作为举例使其概念清晰)

举例2:

函数_参数_02

我们需要自己建立一个头文件:Add.h和一个源文件:Add.c

头文件中放入函数声明:

#ifndef __ADD_H__
#define __ADD_H__

int Add(int x,int y);

#endif


注:其中#ifndef __ADD_H__ #define __ADD_H__#endif是作为标准格式存在的 如果不写 影响不大

源文件中放入函数定义:

int Add(int x,int y)
{
int z=x+y;
return z;
}

而如果我们想要去调用的时候就可以直接在所需调用的代码那输入:

#include"Add.h"

注:自己定义的头文件引用时不能用< >,只能用"  "

8.函数的递归

什么是递归?

程序调用自身的编程技巧称为递归(recursion)。递归作为一种算法在程序设计语言中广泛应用。一个过程或函数在其定义说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可以描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。(也就是说 一个函数自己去调用自己)

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

递归的两个必要条件

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

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

举例:

#include <stdio.h>
int main()
{
printf("hehe\n");
main();
return 0;
}

只不过函数调用一直在栈区申请空间 最后栈区溢出了

函数_参数_03

补充:内存的一大块空间会大概分为:栈区,堆区,静态区

栈区:局部变量;函数的形参

堆区:动态开辟的内存 (如malloc、calloc)

静态区:全局变量;static修饰的变量

举例1:

接收一个整型值(无符号),按照顺序打印它的每一位。例如:1234,输出1 2 3 4 

#include<stdio.h>
void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ",n%10);
}
int main()
{
unsigned int num=0;
printf("请输入一个数:");
scanf("%d",&num);
//递归
print(num);
//拆
//print(1234)
//print(123)--4
//print(12)--3 4
//print(1)--2 3 4
return 0;
}

主要部分:

void print(int n)
{
if(n>9)
{
print(n/10);
}
printf("%d ",n%10);
}

对主要部分进行循环:

函数_函数_04

总而言之:最后会导致print从最内部开始向外依次进行printf的输出,也就是将数字的最左端的那个数开始依次向右进行输出

个人建议多思考链式反应的那个打印4321的那个例子

举例2

自己编写一个函数,不允许创建临时变量,求字符串长度

#include<stdio.h>
int my_strlen(char* str)
{
//my_strlen("hi!")
//1++my_strlen("i!")
//1+1+my_strlen("!")
//1+1+1+my_strlen("")
//1+1+1+0
if(*str!='\0')
return 1+my_strlen(str+1);
else
return 0;
}
int main()
{
char arr[]="hi!";
int len=my_strlen(arr);
printf("len=%d\n",len);
return 0;
}

关键部分:

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

*str表示地址内存的内容,只有不是字符串结束标志’\0‘则会进入if

由于:

//my_strlen("hi!")
//1++my_strlen("i!")
//1+1+my_strlen("!")
//1+1+1+my_strlen("")
//1+1+1+0

所以:

return 1+my_strlen(str+1);

直到走到’\0‘,则返回return 0,进行最后结果的输出

递归与迭代

迭代:重复的做某个事,不一定是循环,如 :goto可以是迭代但goto不是一个循环

个人理解:迭代就是写递推公式

(例子可查看我的文章:“​​迭代求n的阶乘​​​“"​​递归与迭代:求第n个斐波那契数(不考虑溢出)​​")