基础
函数与变量一样,必须先声明再使用。如果函数写在调用函数之前,则不需要进行声明。函数声明格式与java中抽象函数类似,但不需要写形参名,也叫函数原型。如下:
#include <stdio.h>
int sum(int,int);//函数声明
int main(int argc, const char * argv[]) {
int a = 256;
char b = 127;
printf("%d",sum(a,b));//调用函数
return 0;
}
int sum(int a,int b){//定义函数
return a + b;
}
分类
函数原型
1,函数原型的声明即可在调用函数的外面,也可在调用函数之内。
int main(void){
int imin(int,int);//在内部声明函数原型
imin(3,5);
}
2,如果函数不需要参数,需在圆括号内传入void关键字,而不是直接写()。因为不传的话,编译器不会进行参数校验。
3,对于可变参数其原型定义为:返回值 函数名(可确定类型的类型,...);,如int printf(char*,...);这种类型表明第一个参数为字符串,而后面的参数不确定。
递归
递归就是当前函数自己调用自己。其主要有以下性质:
1,位于递归调用前的语句与各级被调函数具有相同的执行顺序。
2,位于递归调用后的语句的执行顺序与各级被调函数的顺序相反。如第1级调用第2级,第2级调用第3级……,那么在递归函数之前的语句的执行顺序就是:第1级的先执行,第2级的次之,第3级的再次之,依此类推;而位于递归后的语句执行顺序为:最后一级先执行,倒数第二级次之,倒数第三级再次之,依此类推。如下函数为计算二进制:
void count(unsigned long num){
int b = num % 2;
if(num >= 2){
count(num/2);
}
printf("%d",b);
}
说明:一个数%2就是它最低位的值(奇数的最低位肯定为1,偶数的最低位肯定是0)。如果一个数不小于2,那么该数还可以拆分成二进制(0,1的二进制分别为其本身),计算完最低位后需要再计算其余位的二进制数,因为递归当前数除以2的值。
函数指针
基础
函数也有地址,因为函数的机器语言实现是由载入到内存的代码组成的,因为函数也有起始地址。函数指针中存储的就是该函数的起始地址。它主要用于将一个函数当作另一个函数的参数进行传递。
声明
与声明指向数据的指针类似,声明函数指针时也必须声明它指向的函数类型。而函数类型要指向函数的返回类型及参数类型。
如void toUpper(char*)函数,它的指针声明为void (*pf)(char*),第一个小括号表明pf是一个指针,小括号外面的为该指针指向的类型。第二个小括号又表明指针指向的是函数,并且参数为char*类型,同时返回值为void。如果除掉第一个小括号,那么代表pf是一个函数,它的返回值为void*,参数为char*。
从上面可以看出,声明一个函数指针最简单的方法就是:在函数原型中,用(*pf)形式的表达式代替掉函数名。这样就声明了一个指向特定类型的函数的函数指针。
赋值及使用
有了函数指针之后,就需要将函数的地址赋值给该指针,在这种场合中,函数名就代表着函数的首地址,这点和数组名一样。
函数指针指向了函数,那么也可以使用指针来访问函数。如下:
void test() {
puts("test");
}
int main(void) {
void (*pf)() = test;//函数代表地址,直接赋值给pf即可
(*pf)();//使用指针访问函数
pf();//也可以这样用
}
使用指针访问函数有上述两种形式:使用fp()或者(*fp)()形式来调用函数,但建议使用第一种。
内联函数
调用函数通常需要一定的时间开销:建立调用、传递参数、跳转到函数并返回。为了减少函数调用时间,可以使用宏函数。c99还提供了另一种方法:内联函数。
创建方法:在函数声明中使用inline关键字进行修饰。首次使用内联函数前,在文件中对内联函数进行定义。编译器看到内联函数的声明后,会使用函数体代替函数的调用,其效果如同在函数调用处输入了内容函数的函数体。
inline void haha(){
puts("haha");
}
void test() {
haha();
}
注意
1,内联函数没有预留它的代码块,所以无法获取内联函数的地址。
2,内联函数应该比较短小,不是必须短小。因为对于很长的函数,调用函数的时间比函数执行所花费的时间少很多,此时使用内联函数就很没必要了。
3,内联函数的定义和调用必须处于同一文件中。因此,内联函数必须具有内部链接(防止别的文件中也定义了相同的函数)。如果多个文件使用相同的内联函数,应该将内联函数写在头文件中(头文件中一般不写可执行代码,但对内联函数是个例外),然后这些文件引用相同的头文件。