在进行数值算法的程序设计时,往往需要实现程序的通用性,即同一个程序可以对不同的问题进行求解,如:计算函数f(x)的数值积分的程序,我们需要写一个通用的数值积分程序——Integr(),使其可以对不同的f(x)函数均可以进行积分计算,而不需要每计算一个函数的积分就重新写一个Integr()程序,仅需要另写一个f(x)的表达式程序即可。
若使用Matlab实现,则显然少不了eval(),和feval()函数的使用,即把需要进行积分计算的函数f(x)写完之后保存为.m文件,然后在积分程序中只需要把f(x)的文件名作为参数传递给Integr()即可。
若是把f(x)函数名作为字符串传入,则在Integr()中只需将f(x)函数名及其参数写为一个字符串,使用eval(字符串)执行即可,如:
|
function result=integr(a,b,n,fun) %定义积分函数,a为积分下限,b为积分上限,n为划分区间的个数,fun为需进行计算
%的函数的文件名 h=(b-a)/n; x=[a:h:b]; y=eval([fun,'(x)']); %将函数名fun和其参数x合成一个字符串并执行字符串,即执行计算fun(x),
%注意不能丢了fun的括号。 result=sum(y);
|
定义需要积分计算的函数fun之后,直接作为integr()的参数引用即可,如任意定义函数如下:
|
function r=ff(x) r=sin(x).*(1+x.*x);
|
则,在命令窗口引用即可:将'ff'换为其他函数名字符串即可进行计算其数值积分。
使用feval()函数则更加方便,integr()函数如下定义:
|
function result=integr(a,b,n,fun) h=(b-a)/n; x=[a:h:b]; y=fun(x);%fun为传入的函数指针,可以直接作为引用 result=sum(y);
|
则可以通过如下方式使用:
|
integr(0,1,1000,@ff)%‘@’即表示ff作为函数指针传入
|
Matlab为通过命令解释方式执行程序,而不需要对源程序进行编译,因此可以将函数名作为一个字符串参数传入,然后执行时将字符串解释为函数命令即可。而其他语言,如C语言则必须首先进行编译,得到可执行文件后再使用,因此在C语言中无法采用将函数名作为字符串的形式作为字符参数传入。因此便想到了C语言的函数指针,当时在初学C语言时对函数指针并没有重视,现在重新回顾函数指针,发现通过它便可解决数值计算程序的通用性的问题。仍然以数值积分为例:
integr()函数的定义如下:(为了方便直接保存为integr.h)
|
#include <stdio.h> #include <stdlib.h> #include "math.h" double integr(double a,double b,int n,double (*f)(double)); double integr(double a,double b,int n,double (*f)(double)) { int i; double y=0,h,x; h=(b-a)/n; x=a; for(i=0;i<=n;i++) { y+=(*f)(x);/*****此处对函数指针f的引用方式比较灵活,可以使用如上:y=(*f)(x)的方式,也可以直接使用指针名引用,即:y=f(x) ******/ x+=h; } y*=h; return y; }
|
任意定义一被积函数ff如下:(为了方面被主函数包含,保存为ff.h)
|
#include <stdio.h> #include <math.h> double ff(double x); double ff(double x) { return 2*x*(1+x-x*x); }
|
由于C语言不能通过命令解释方式执行,必须编译为可执行文件,因此需具有主函数,这里定义一个可接受参数的main()函数,如下:
|
#include <stdio.h> #include <math.h> #include <stdlib.h> #include "integr.h" #include "ff.h" int main(int argc,char *argv[])/***由argv[]接受积分参数****/ { int n; double a,b; if(argc!=4)/****由外部传入3个参数,加上程序名argv[0]共四个,否则提示出错***/ { printf("parameters error!!\n"); return 0; } /***argv[0]为所运行的程序的文件名****/ a=atof(argv[1]);/**将第二个参数argv[1]转化为积分下限a**/ b=atof(argv[2]);/**将第三个参数argv[2]转化为积分上限b**/ n=atoi(argv[3]);/**将第四个参数argv[3]转化为积分区间个数n**/ printf("result=%f\n",integr(a,b,n,ff));/**此处对ff可以直接引用其函数名ff,也可以先定义一个函数指针变量在对函数指针变量进行赋值和引用***/ }
|
编译测试,命令行运行main()函数,如下:
由于函数名本身就是函数的首地址,所以函数名与函数指针本质上是等效的,函数名是一个指针常量,而自定义的函数指针是一个指针变量,所以函数名和函数指针的赋值与引用方式比较灵活。如:
定义函数指针:
|
double (*fun)(double); /**fun为一个指向带有一个double型参数,返回值为double型的函数的指针**/
|
为指针赋值:
|
fun=&ff;/**将ff的地址值赋给fun,这种赋值方式与直接用:fun=ff; 是等价的**/
|
引用指针:
|
y=(*fun)(x);/**这种引用方式与 y=fun(x);也是等价的**/
|
我们一般对函数的引用都采用类似数学函数的方式,如求-5的绝对值,即:
也可以使用:
的方式,两种方式等价,但显然前一种方式更方便一些。
熟悉了函数名作为参数传递的方式,便可以把一些数值算法写为通用程序了,在C语言中算法程序是通用的,如上述integr(),但主程序(main()函数)仍然需要每次重新改写,main()函数一般仅有几句赋值与引用的代码即可,并不麻烦。
(以上C程序为在Linux系统gcc/g++编译环境下测试)