目录
一、库函数
1.什么是函数
f(x) =2*x+c
维基百科中,将函数定义为子程序,在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。一般会有输入参数并有返回值(输出),提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
C语言中函数的分类,分为库函数和自定义函数
库函数就是将我们经常使用的功能设置为库函数,从而提高编译速率
2.库函数的分类
IO函数:printf scanf getchar
字符串操作函数:strcmp strlen
字符操作函数: toupper (小写转大写)
内存操作函数:memcpy memcmp memset
时间/日期函数: time
数学函数: sqrt pow
其他库函数
3.库函数
两个库函数的学习网站,用于查询各种函数的使用方法
注:
但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。
这里对照文档来学习上面几个库函数,目的是掌握库函数的使用方法。
4.库函数使用举例
(1)strcpy函数的使用
将源数组的字符串 复制到 目标数组中
如图:将arr2 中的字符串 复制到 arr1 中
(2)memset ——memory set 内存设置
将被ptr所指向的内存块中的前 num 个字符 设置为特殊值
如图:将数组arr 中的前 5 个字符改为 ‘x’。
(3)scanf函数使用
scanf("输入控制符", 输入参数);
scanf("%type",&name)
举例:
1:单个字符或整形
char a; int a
scanf("%c",&a); //只能输入一个字符。
scanf("%d",&a); //只能输入一个整形。
2:字符串
char b[20];
scanf("%s",b); //可以输入一串不超过20字符的字符串
3:数组
在输入数组时,谨记:
scanf("%d\n",&n);
int a[6]; int k; for(int k=0;k<6;k++) { scanf("%d",&a[k]); }
数组名虽然也是地址,但如果按scanf("%d\n",a)输入,会造成编译器把六个数字会当成一个数字输进去。
二、自定义函数
1.自定义函数
自定义函数和库函数一样,由函数名,返回值类型和函数参数组成。
2.函数的组成
ret_type fun_name(para1,* )
{
statement;//语句项
}
ret_type 返回类型
fun_name 函数名
para1 函数参数(可有多个)
注:
函数在运行完之后,要给出具体的返回类型,如果没有那类型写为 void,void表示这个函数不返回任何值,也不需要返回
理解思路:我(main函数)让张三(自定义函数)找出最大值,张三怎么找我不管,但必须返回给我具体的值(z),z 的类型必须与函数一致,观察下面代码。
3.函数的参数
(1)实际参数(实参):
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。下面是几种实参的表示:
(2)形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。(如局部变量)
参考例子3
4.函数的调用
(1)传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
当只是传输值时,利用传值调用。
(2)传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
传址调用的时候,可以通过形参来操作实参。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。
(3)写一个函数交换两个整形变量的值
错误写法:
在主函数中,a和b各占一个地址;当将a和b的值引入swap函数中的x和y中时,x和y存到了另外两个地址中,因此函数中 x ,y值的交换与主函数中的a,b没有了关系。
swap在被调用的时候,实参传给形参,其实形参是实参的一份临时拷贝,改变形参,不能改变实参
如图:ab和xy的地址并不相同,因此两者无真正联系。
正确写法:
利用 指针 int*pa=&a int*pb=&b
将a,b的地址传到swap函数中,利用指针可直接交换a,b地址中的值;
将a的值10引入到*pa,b的值引入*pb,整个过程,数值只在a,b和z的地址中交换,不换产生其他地址。
(4)总结:
函数返回类型的地方写出:void表示这个函数不返回任何值,也不需要返回
什么时候传地址,运行过程中变量之间发生值的调换,函数内部和外部之间必须传相应的地址(指针*p)
5.函数的嵌套调用和链式访问
C语言中函数的定义都是相互平行、相互独立的,也就是说在函数定义时,函数体内不能包含另一个函数的定义,即函数不能嵌套定义,但可以嵌套调用。
嵌套函数,就是指在某些情况下,您可能需要将某函数作为另一函数的参数使用,这一函数就是嵌套函数。
(1)嵌套调用
首先看一下嵌套定义,像下面这种就是错误的,不管在主函数main中,还是非主函数中嵌套定义,都是错误的。
int main()
{
int add(int a,int b)
{
return a+b;
}
printf("result = %d\n",add(5,67));
return 0;
}
如下面的代码中,函数test1调用了函数test2,函数test2调用了函数test3。
(2)链式访问
实质:将函数的返回值变成其他函数的参数
printf 函数返回值是打印字符的个数
由内到外,首先打印的字符有43\n共3个字符,于是中间输出3,最外侧输出2
由内到外,首先打印43共两个字符,于是中间输出2,最外侧输出1
6.函数声明与定义
(1)函数声明:
什么是函数声明
a.告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。(如图)
b.函数的声明一般出现在函数的使用之前。要满足先声明后使用。(如果函数先定义,再使用,则不用声明(如图))
c.函数的声明一般要放在头文件中的。
例:写一个计算机代码
未来写代码都是分模块来完成,比如写一个计算机代码,分四个模块
加法--A;减法--B;乘法--C;除法--D
如:加法模块创建自己的模块
另创建头文件 add.h sub.h 和源文件add.c sub.c,在主程序中需要引用
#include“ ——” == int Add(int x, int y);
内容分别如下:
头文件用来声明,源文件用于定义。
7.函数递归
7.1什么是递归?
程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接
调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,
递归策略:
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:大事化小,就像考虑数列一样,找数列F(n-1)~F(n)~F(n+1)等关系
注:如果递归主函数就会陷入死递归
7.2递归的两个必要条件
1.存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2.每次递归调用之后越来越接近这个限制条件。
注意是必要条件,不是说有这两个条件,就一定递归
如果递归次数超过了栈区内存,就会出现 Stack Overflow (栈溢出);
【引申】:什么是栈溢出
首先我们要知道内存区域的分配,了解栈区;
每一次递归函数都会在栈区开辟自己的空间给 main 函数
test函数每一次运行都会开辟一次空间,当超过栈区内存时,就会溢出
【要求】递归代码
避免死递归,要有跳出条件和逼近跳出条件
递归层次不能太深,避免栈溢出
【分享】程序员的知乎!
提问写代码遇到的问题
注: