函数是指一段在一起的、可以做某一件事的程序,也叫子程序、方法。
定义:
返回类型 函数名字(形式参数)
{
代码块
}
形式参数列表包括变量名和它们的类型声明。
代码块包含了局部变量的声明和相关执行语句。
compare.c中1
2
3
4
5
6
7
8
9
10
11int max(int value_left, int value_right)
{
if (value_left > value_right)
{
return value_left;
}
else
{
return value_right;
}
}
如果函数的类型声明为void,没有返回值;否则,需要相应类型的返回值。
声明:
当我们在一个源文件中定义的函数想被其他源文件使用的话,我们需要在头文件中声明这个函数,想调用这个函数的源文件中,include这个头文件即可。
.h文件中按如下方式进行声明。
返回类型 函数名字(形式参数);1
2
3
4
5
6
7
8
9
10internel.h中
int max(int value_left ,int value_right);
在 main.c中使用
int main(void)
{
int max_value;
max_value(10,20);
printf(“max value is %d\n”,max_value);
}
函数形参:
C函数的所有参数均以“传值调用”方式进行传递。但是当我们传进来的参数为一个数组时,并在函数㕜使用下表引用该数组的参数,那么实际上函数对该数据的修改是调用程序中数组的元素,这个行为被称为“传址调用”。
下面这个数组,length为传值调用,array为传址调用1
2
3
4
5
6
7
8
9
10int array[10];
void reset(int *array,int length)
{
while (length >0)
{
/*把数组的元组清零*/
array[lengh] = 0;
length—;
}
}
递归与迭代:
递归算法:
直接或间接调用自身的函数。
把操作的内容放入栈,等函数递归完成后,出栈打印
下面这个简单的例子来说明递归调用方式。
程序作用:输入一个值,循环打印它的除以10 后剩余的值。1
2
3
4
5
6
7
8
9
10
11
12
13
14void fun(int input)
{
if (input >0)
{
fun(input/10);
}
printf("%d\n",input);
}
int main(void)
{
fun(1234);
return 0;
}
输出:
0
1
12
123
1234
这里递归的操作依次为,先把第一个input放入栈,此时为1234;当再次调用时得到input的值,input为123,把123放入栈,一直大牌最后input的值为0,结束递归,打印时,先从栈顶取出数据,栈顶数据为0,栈底数据为1234,打印上面输出的结果
数据操作如下图所示:
迭代算法:
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
对于上面递归的算法,我们换成迭代来实现:1
2
3
4
5
6
7
8
9
10void fun(int input)void fun(int input)
{
int ret;
while(input >0)
{
ret = input;
printf("%d\n",ret);
input = input/10;
}
}
总结:
递归的使用可以使代码更加简洁清晰,可读性更好,但由于递归需要系统堆栈,所以空间消耗比非递归代码要大很多。
理论上,递归与迭代在时间复杂度方面是等价的,但实际上递归的效率确实比迭代低。
一般情况下,能不用递归我们就尽量不使用递归,我们使用递归的先决条件是,当且仅当一个存在预期的收敛时,我们才使用递归算法,否则我们还是用迭代。
可变参数:
我们之前将的函数传进来的参数个数都是固定的,那么我们有没有办法,函数的参数有多个,但是我们只调用其中一部分呢,C的可变参数实现了这一功能。
我们看下面这个例子,msg函数有多个参数,但是我们只调用其中一个,这种方式我们可以用在log打印上,可以打印不同类型的log,以及多个参数列表实现。1
2
3
4
5void msg(int x,int y…);
void main(void)
{
msg(10);
}
C的可变参数使用,需要用到下面一些函数:
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
va_list:用来保存宏va_start、va_arg和va_end所需信息的一种类型。为了访问变长参数列表中的参数,必须声明va_list类型的一个对象 定义: typedef char * va_list;
va_start:访问变长参数列表中的参数之前使用的宏,它初始化用va_list声明的对象,初始化结果供宏va_arg和 va_end使用;
va_arg: 展开成一个表达式的宏,该表达式具有变长参数列表中下一个参数的值和类型。每次调用va_arg都会修改用va_list声明的对象,从而使该对象指向参数列表中的下一个参数;
va_end:该宏使程序能够从变长参数列表用宏va_start引用的函数中正常返回。
va在这里是variable-argument(可变参数)的意思.
这些宏定义在stdarg.h中,所以用到可变参数的程序应该包含这个头文件.下面我们写一个简单的可变参数的函数,改函数至少有一个整数参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.
例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
void msg_print(char *msg,...)
{
va_list arg_ptr; /* 定义保存函数参数的结构 */
int ipara =0; /* 定义参数个数 */
char *para = NULL;
char flag;
/* arg_ptr指向传入的第一个可选参数,msg是最后一个确定的参数 */
va_start(arg_ptr, msg);
while(*msg)
{
flag = *msg++;
if (flag != '%')
{
putchar(flag); /* 如果字符不包括%号,把字符输出到终端上 */
continue;
}
flag = *msg++; /* 如果字符是%号,指针后移一位,判断对应格式 */
switch(flag)
{
case 's': /* 检索到%s,进行字符串的打印 */
para = va_arg(arg_ptr, char *); /* 取出当前的参数,类型为char *. */
printf("%s",para);
break;
case 'd':
ipara = va_arg(arg_ptr, int);
printf("%d", ipara);
default:
break;
}
}
va_end(arg_ptr);
}
int main(void)
{
msg_print("The first print %d\n",100);
msg_print("The second print %s,%d\n","hello",200);
return 0;
}
输出:
he first print 100
The second print hello,200