C语言中的函数指针基础学习教程
顾名思义,函数指针就是函数的指针。它是一个指针,指向一个函数。看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun2(char * p1,char * p2); C) char * fun3(char * p1,char * p2); 看看上面三个表达式分别是什么意思? C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型。 int (*)[10] p; 再看看A)表达式与这里何其相似!明白了吧。这里fun1不是什么函数名,而是一个指针变量,它指向一个函数。这个函数有两个指针类型的参数,函数的返回值也是一个指针。同样,我们把这个表达式改写一下: char * (*)(char * p1,char * p2) fun1; 这样子是不是好看一些呢?只可惜编译器不这么想。^_^。
我们从一个非常简单的”Hello World“函数入手,来见识一下怎样创建一个函数指针。 #include <stdio.h> // 函数原型 void sayHello(); //函数实现 void sayHello(){ printf("hello worldn"); } // main函数调用 int main() { sayHello(); } 我们定义了一个名为sayHello的函数,它没有返回值也不接受任何参数。当我们在main函数中调用它的时候,它向屏幕输出出”hello world“。非常简单。接下来,我们改写一下main函数,之前直接调用的sayHello函数,现在改用函数指针来调用它。 int main() { void (*sayHelloPtr)() = sayHello; (*sayHelloPtr)(); } 第二行void (*sayHelloPtr)()的语法看起来有些奇怪,我们来一步一步分析。 这里,关键字void的作用是说我们创建了一个函数指针,并让它指向了一个返回void(也就是没有返回值)的函数。 在第二行被声明之后,sayHelloPtr作为函数指针的名称,跟其他任何指针没有差别,能够储值和赋值。 int main() { void (*sayHelloPtr)() = sayHello; sayHelloPtr(); } 跟之前一样,我们将sayHello函数赋给函数指针。但是这一次,我们用调用普通函数的方法调用了它。稍后讨论函数名的时候我会解释这一现象,现在只需要知道(*syaHelloPtr)()和syaHelloPtr()是相同的即可。 带参数的函数指针 好了,这一次我们来创建一个新的函数指针吧。它指向的函数仍然不返回任何值,但有了参数。 #include <stdio.h> //函数原型 void subtractAndPrint(int x,int y); //函数实现 void subtractAndPrint(int x,int y) { int z = x - y; printf("Simon says,the answer is: %dn",z); } //main函数调用 int main() { void (*sapPtr)(int,int) = subtractAndPrint; (*sapPtr)(10,2); sapPtr(10,2); } 跟之前一样,代码包括函数原型,函数实现和在main函数中通过函数指针执行的语句。原型和实现中的特征标变了,之前的sayHello函数不接受任何参数,而这次的函数subtractAndPrint接受两个int作为参数。它将两个参数做一次减法,然后输出到屏幕上。 在第14行,我们通过'(*sapPtr)(int,int)'创建了sapPtr这个函数指针,与之前的区别仅仅是用(int,int)代替了原来的空括号。而这与新函数的特征标相符。
这一次,我们把subtractAndPrint函数改成一个名为subtract的函数,让它把原本输出到屏幕上的结果作为返回值。 #include <stdio.h> // 函数原型 int subtract(int x,int y); // 函数实现 int subtract(int x,int y) { return x - y; } // main函数调用 int main() { int (*subtractPtr)(int,int) = subtract; int y = (*subtractPtr)(10,2); printf("Subtract gives: %dn",y); int z = subtractPtr(10,z); } 这与subtractAndPrint函数非常相似,只是subtract函数返回了一个整数而已,特征标也理所当然的不一样了。 在第13行,我们通过int (*subtractPtr)(int,int)创建了subtractPtr这个函数指针。与上一个例子的区别只是把void换成了int来表示返回值。而这与subtract函数的特征标相符。 把函数指针作为参数来传递 我们已经了解过了函数指针声明和执行的各种情况,不论它是否带参数,或者是否有返回值。接下来我们利用一个函数指针来根据不同的输入执行不同的函数。 #include <stdio.h> // 函数原型 int add(int x,int y); int subtract(int x,int y); int domath(int (*mathop)(int,int),int x,int y); // 加法 x+ y int add(int x,init y) { return x + y; } // 减法 x - y int subtract(int x,int y) { return x - y; } // 根据输入执行函数指针 int domath(int (*mathop)(int,int y) { return (*mathop)(x,y); } // main函数调用 int main() { // 用加法调用domath int a = domath(add,10,2); printf("Add gives: %dn",a); // 用减法调用domath int b = domath(subtract,2); printf("Subtract gives: %dn",b); } 我们来一步一步分析。 我们有两个特征标相同的函数,add和subtract,它们都返回一个整数并接受两个整数作为参数。 函数名和地址 既然有约在先,那我们就讨论一下函数名和地址作为结尾吧。一个函数名(或称标签),被转换成了一个指针本身。这表明在函数指针被要求当作输入的地方,就能够使用函数名。这也导致了一些看起来很糟糕的代码却能够正确的运行。瞧瞧下面这个例子。 #include <stdio.h> // 函数原型 void add(char *name,int y); // 加法 x + y void add(char *name,int y) { printf("%s gives: %dn",name,x + y); } // main函数调用 int main() { // 一些糟糕的函数指针赋值 void (*add1Ptr)(char*,int,int) = add; void (*add2Ptr)(char*,int) = *add; void (*add3Ptr)(char*,int) = &add; void (*add4Ptr)(char*,int) = **add; void (*add5Ptr)(char*,int) = ***add; // 仍然能够正常运行 (*add1Ptr)("add1Ptr",2); (*add2Ptr)("add2Ptr",2); (*add3Ptr)("add3Ptr",2); (*add4Ptr)("add4Ptr",2); (*add5Ptr)("add5Ptr",2); // 当然,这也能运行 add1Ptr("add1PtrFunc",2); add2Ptr("add2PtrFunc",2); add3Ptr("add3PtrFunc",2); add4Ptr("add4PtrFunc",2); add5Ptr("add5PtrFunc",2); } 这是一个简单的例子。运行这段代码,你会看到每个函数指针都会执行,只是会收到一些关于字符转换的警告。但是,这些函数指针都能正常工作。 在第15行,add作为函数名,返回这个函数的地址,它被隐式的转换为一个函数指针。我之前提到过,在函数指针被要求当作输入的地方,就能够使用函数名。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |