目录
一、函数的定义
二、函数的分类
1、库函数
2、自定义函数
三、函数的参数
1、实际参数(实参)
2、形式参数(形参)
四、函数的调用
1、传值调用
2、传址调用
五、函数的嵌套调用和链式访问
1、嵌套调用
2、链式访问
六、函数的声明和定义
1、函数的声明
2、函数的定义
七、函数递归
1、递归定义
2、递归的必要条件
3、实例
维基百科中对函数的定义:子程序
子程序,是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
首先为什么会有库函数?
1. 我们在学习C语言的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格 式打印到屏幕上(printf)。
像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到, 为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发。
查询库函数的工具:http://www.cplusplus.com
C语言常用的库函数都有:
字符串操作函数
字符操作函数
内存操作函数
时间/日期函数
数学函数
其他库函数
自定义函数和库函数一样要有函数名、返回值类型和函数参数
自定义函数的组成
ret_type fun_name(para1, * )
{
statement;//语句项
}
//ret_type 返回类型
//fun_name 函数名
//para1 函数参数
接下来我们举两个例子来看一下
(1)求出a和b中较大的数
#include
int get_max(int x, int y)//int:返回类型 get_max:函数名//x,y:函数参数
{
if (x > y)
return x;
else
return y;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
int m = get_max(a, b);
printf("%d", m);
return 0;
}
(2)交换a和b
#include
void swap(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0;
int b = 0;
printf("交换前:");
scanf("%d %d", &a, &b);
swap(a, b);
printf("交换后:");
printf("%d %d", a, b);
return 0;
}
可是它运行的结果是:
好像并没有交换成功,这是为什么呢?
当函数调用时,实参传给形参,但形参只是实参的一份临时拷贝,对形参地修改是不影响实参的!
形参x和y都有自己独立的空间,x和y的交换是不会影响a和b的,所以重点是要xy和ab建立联系
如何建立联系呢?把a、b的地址传过去放在指针变量*px和*py中,px、py就分别得到了a和bde地址,*px,*py就有了远程操控的作用,a和b才能实现交换!
所以正确的代码应该是这样的
#include
void swap(int* px, int* py)
{
int t = 0;
t = *px;
*px = *py;
*py = t;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
swap(&a, &b);
printf("%d %d", a, b);
return 0;
}
真实传给函数的参数,叫实参。 实参可以是:常量、变量、表达式、函数等。 无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内 存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外边的变量。
接下来我举两个例子
1、写一个函数,判断一个数是否为素数
#include
//判断是否是素数,是素数返回1,不是返回0
int is_primer(int n)
{
//试除
int j = 0;
for (j = 2; j <=sqrt(n); j++)
{
if (n % j == 0)
{
return 0;
}
}
return 1;
}
int main()
{
int i = 0;
for (i = 100; i <= 200; i++)
{
if (is_primer(i) == 1)
{
printf("%d ", i);
}
}
return 0;
}
2、写一个函数,实现一个整形有序数组的二分查找。
#include
int binary_search(int a[], int k, int size)
{
int left = 0;
int right = size - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (k > a[mid])
{
left = mid + 1;
}
else if (k < a[mid])
{
right = mid - 1;
}
else if (k = a[mid])
{
return mid;//找到了
}
}
return -1;
}
int main()
{
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int size = sizeof(a) / sizeof(a[0]);
int k = 0;
scanf("%d", &k);
int ret = binary_search(a, k, size);
if (ret == -1)
{
printf("找不到");
}
else
{
printf("找到了下标是%d", ret);
}
return 0;
}
函数可以嵌套调用,但不能嵌套定义
把一个函数的返回值作为另外一个函数的参数
#include
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
答案是:4321
1、printf("%d",43),打印43
2、printf(“%d”,printf("%d",43)),因为printf函数的返回值是打印在屏幕上字符的个数,43字符个数是2,它等价于printf'("%d",2),所以打印2
3、printf("%d",printf(“%d”,printf("%d",43))),2字符个数是1,即printf("%d",1),所以打印1
所以最终结果就是“4321”
a.函数的声明一般出现在函数的使用之前。要满足先声明后使用
b.函数的声明一般要放在头文件中
函数声明
int Add(int, int);//提前声明
int main()
{
int a = 0;
int b = 0;
int s = Add(a, b);
printf("%d", s);
return 0;
}
int Add(int x, int y)
{
return x + y;
}//定义到后面需要提前声明
函数的定义是指函数的具体实现,交待函数的功能实现。
test.h放函数的声明
test.c放函数的实现
程序调用自身的编译技巧(大事化小)。
a.存在限制条件,当满足这个限制条件的时候,递归便不再继续。
b.每次递归调用之后越来越接近这个限制条件。
接受一个整型值(无符号),按照顺序打印它的每一位。
1、方法一
#include
//自己调用自己,就是递归
打印每一位
int main()
{
unsigned int num = 0;
scanf("%d", &num);
//1234%10=4
//1234/10=123
//123%10=3
//123/10=12
//12%10=2
//12/10=1
//1%10=1
//1/10=0
while (num)
{
printf("%d", num % 10);
num = num / 10;
}
}
2、方法二(递归)
递归的思想方式
#include
void print(unsigned int n)
{
if (n < 10)
printf("%d ", n);
else {
print(n / 10);
printf("%d ", n % 10);
}
}
//void print(unsigned int n)//第二种递归
//{
// if (n > 9)
// {
// print(n / 10);
// }
// printf("%d ", n % 10);
//}
int main()
{
unsigned int n = 0;
scanf("%d", &n);
print(n);
}
<如有错误请批评指正>