首页 彻底搞定C指针

彻底搞定C指针

举报
开通vip

彻底搞定C指针 第 3篇--指针与数组名 1. 通过数组名访问数组元素 看下面代码 int i,a[]={3,4,5,6,7,3,7,4,4,6}; for (i=0;i<=9;i++) { printf ( “%d”, a[i] ); } 很显然,它是显示 a 数组的各元素值。 我们还可以这样访问元素,如下 int i,a[]={3,4,5,6,7,3,7,4,4,6}; for (i=0;i<=9;i++) { printf ( “%d”,*(a+i) ); } 它的...

彻底搞定C指针
第 3篇--指针与数组名 1. 通过数组名访问数组元素 看下面代码 int i,a[]={3,4,5,6,7,3,7,4,4,6}; for (i=0;i<=9;i++) { printf ( “%d”, a[i] ); } 很显然,它是显示 a 数组的各元素值。 我们还可以这样访问元素,如下 int i,a[]={3,4,5,6,7,3,7,4,4,6}; for (i=0;i<=9;i++) { printf ( “%d”,*(a+i) ); } 它的结果和作用完全一样 2. 通过指针访问数组元素 int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6}; pa =a;//请注意数组名 a直接赋值给指针 pa for (i=0;i<=9;i++) { printf ( “%d”, pa[i] ); } 很显然,它也是显示 a 数组的各元素值。 另外与数组名一样也可如下: int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6}; pa =a; for (i=0;i<=9;i++) { printf ( “%d”, *(pa+i) ); } 看 pa=a即数组名赋值给指针,以及通过数组名、指针对元素的访问形式看,它们并没有什么区别, 从这里可以看出数组名其实也就是指针。难道它们没有任何区别?有,请继续。 3. 数组名与指针变量的区别 请看下面的代码: int i,*pa,a[]={3,4,5,6,7,3,7,4,4,6}; pa =a; for (i=0;i<=9;i++) { printf ( “%d”, *pa ); pa++ ;//注意这里,指针值被修改 } 可以看出,这段代码也是将数组各元素值输出。不过,你把{}中的 pa改成 a试试。你会发现程序 编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而数组名只是一 个指针常量。这个代码与上面的代码不同的是,指针 pa在整个循环中,其值是不断递增的,即指针值 被修改了。数组名是指针常量,其值是不能修改的,因此不能类似这样操作:a++。前面 4,5节中 p a[i],*(pa+i)处,指针 pa的值是使终没有改变。所以变量指针 pa与数组名 a可以互换。 4. 申明指针常量 再请看下面的代码: int i, a[]={3,4,5,6,7,3,7,4,4,6}; int * const pa=a;//注意 const的位置:不是 const int * pa, for (i=0;i<=9;i++) { printf ( “%d”, *pa ); pa++ ;//注意这里,指针值被修改 } 这时候的代码能成功编译吗?不能。因为 pa指针被定义为常量指针了。这时与数组名 a已经没有 不同。这更说明了数组名就是常量指针。但是… int * const a={3,4,5,6,7,3,7,4,4,6};//不行 int a[]={3,4,5,6,7,3,7,4,4,6};//可以,所以初始化数组时必定要这样。 以上都是在 VC6.0上实验。 Stephen_yu 附注 int * const 是指向const 指针,指针的内容不能变,const int * 是const int 型的指针,指向的内容不能变 第 4篇:const int * pi/int * const pi的区别 1 从 const int i 说起 你知道我们申明一个变量时象这样 int i ;这个 i是可能在它处重新变赋值的。如下: int i=0; //… i=20;//这里重新赋值了 不过有一天我的程序可能需要这样一个变量(暂且称它变量),在申明时就赋一个初始值。之后我 的程序在其它任何处都不会再去重新对它赋值。那我又应该怎么办呢?用 const 。 //************** const int ic =20; //… ic=40;//这样是不可以的,编译时是无法通过,因为我们不能对 const 修饰的 i c重新赋值的。 //这样我们的程序就会更早更容易发现问题了。 //************** 有了 const修饰的 ic 我们不称它为变量,而称符号常量,代 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 着 20这个数。这就是 const 的作用。 ic是不能在它处重新赋新值了。 认识了 const 作用之后,另外,我们还要知道 格式 pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载 的写法。有两种:const int ic=20;与 int const ic=20;。它们是完全相同的。这一点我们是要清楚。总之,你务必要记住 const 与 int哪个写前都不影 响语义。有了这个概念后,我们来看这两个家伙:const int * pi与 int const * pi ,按你的逻辑看, 它们的语义有不同吗?呵呵,你只要记住一点,int 与 const 哪个放前哪个放后都是一样的,就好比 c onst int ic;与 int const ic;一样。也就是说,它们是相同的。 好了,我们现在已经搞定一个“双包胎”的问题。那么 int * const pi与前两个式子又有什么不同呢? 我下面就来具体 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 它们的格式与语义吧! 2 const int * pi的语义 我先来说说 const int * pi是什么作用 (当然 int const * pi也是一样的,前面我们说过,它们实 际是一样的)。看下面的例子: //*************代码开始*************** int i1=30; int i2=40; const int * pi=&i1; pi=&i2;//4.注意这里,pi可以在任意时候重新赋值一个新内存地址 i2=80;//5.想想看:这里能用*pi=80;来代替吗?当然不能 printf( “%d”, *pi ) ;//6.输出是 80 //*************代码结束*************** 语义分析: 看出来了没有啊,pi的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*pi 来修改 i2的值。这个规则符合我们前面所讲的逻辑吗?当然符合了! 首先 const修饰的是整个*pi(注意,我写的是*pi而不是 pi)。所以*pi是常量,是不能被赋值的(虽 然 pi所指的 i2是变量,不是常量)。 其次,pi前并没有用 const 修饰,所以 pi是指针变量,能被赋值重新指向另一内存地址的。你可能 会疑问:那我又如何用 const 来修饰 pi呢?其实,你注意到 int * const pi中 const 的位置就大概可 以明白了。请记住,通过格式看语义。哈哈,你可能已经看出了规律吧?那下面的一节也就没必要看 下去了。不过我还得继续我的战斗! 3 再看 int * const pi 确实,int * const pi与前面的 int const * pi会很容易给混淆的。注意:前面一句的 const 是写在 pi前和*号后的,而不是写在*pi前的。很显然,它是修饰限定 pi的。我先让你看例子: //*************代码开始*************** int i1=30; int i2=40; int * const pi=&i1; //pi=&i2;4.注意这里,pi不能再这样重新赋值了,即不能再指向另一个新地址。 //所以我已经注释了它。 i1=80;//5.想想看:这里能用*pi=80;来代替吗?可以,这里可以通过*pi修改 i 1的值。 //请自行与前面一个例子比较。 printf( “%d”, *pi ) ;//6.输出是 80 //***************代码结束********************* 语义分析: 看了这段代码,你明白了什么?有没有发现 pi值是不能重新赋值修改了。它只能永远指向初始化 时的内存地址了。相反,这次你可以通过*pi来修改 i1的值了。与前一个例子对照一下吧!看以下的两 点分析 1). pi因为有了 const 的修饰,所以只是一个指针常量:也就是说 pi值是不可修改的(即 pi不可 以重新指向 i2这个变量了)(看第 4行)。 2). 整个*pi的前面没有 const 的修饰。也就是说,*pi是变量而不是常量,所以我们可以通过*pi 来修改它所指内存 i1的值(看 5行的注释) 总之一句话,这次的 pi是一个指向 int变量类型数据的指针常量。 我最后总结两句: 1).如果 const 修饰在*pi前则不能改的是*pi(即不能类似这样:*pi=50;赋值)而不是指 pi。 2).如果 const 是直接写在 pi前则 pi不能改(即不能类似这样:pi=&i;赋值)。 请你务必先记住这两点,相信你一定不会再被它们给搞糊了。现在再看这两个申明语句 int const *pi和 int * const pi时,呵呵,你会头昏脑胀还是很轻松惬意?它们各自申明的 pi分别能修改什么, 不能修改什么?再问问自己,把你的理解告诉我吧,可以发帖也可以发到我的邮箱(我的邮箱 yyf977 @163.com)!我一定会答复的。 3.补充三种情况。 这里,我再补充以下三种情况。其实只要上面的语义搞清楚了,这三种情况也就已经被包含了。 不过作为三种具体的形式,我还是简单提一下吧! 情况一:int * pi指针指向 const int i常量的情况 //**********begin***************** const int i1=40; int *pi; pi=&i1;//这样可以吗?不行,VC下是编译错。 //const int 类型的 i1的地址是不能赋值给指向 int 类型地址的指针 pi的。否则 pi岂不是能修改 i1的值了吗! pi=(int* ) &i1;//这样可以吗?强制类型转换可是 C所支持的。 //VC下编译通过,但是仍不能通过*pi=80来修改 i1的值。去试试吧!看看具 体的怎样。 //***********end*************** 情况二:const int * pi指针指向 const int i1的情况 //*********begin**************** const int i1=40; const int * pi; pi=&i1;//两个类型相同,可以这样赋值。很显然,i1的值无论是通过 pi还是 i1 都不能修改的。 //*********end***************** 情况三:用 const int * const pi申明的指针 //***********begin**************** int i const int * const pi=&i;//你能想象 pi能够作什么操作吗?pi值不能改,也不 能通过 pi修改 i的值。因为不管是*pi还是 pi都是 const的。 第 5篇:函数 参数 转速和进给参数表a氧化沟运行参数高温蒸汽处理医疗废物pid参数自整定算法口腔医院集中消毒供应 的传递(上) 一. 三道考题 开讲之前,我先请你做三道题目。(嘿嘿,得先把你的头脑搞昏才行……唉呀,谁扔我鸡蛋?) 1. 考题一:程序代码如下: void Exchg1(int x, int y) { int tmp; tmp=x; x=y; y=tmp; printf(“x=%d,y=%d\n”,x,y) } void main() { int a=4,b=6; Exchg1 (a,b) ; printf(“a=%d,b=%d\n”,a,b) } 输出的结果: x=____, y=____ a=____, b=____ 问下划线的部分应是什么,请完成。 2. 考题二:代码如下。 Exchg2(int *px, int *py) { int tmp=*px; *px=*py; *py=tmp; print(“*px=%d,*py=%d\n”,*px,*py); } main() { int a=4; int b=6; Exchg2(&a,&b); Print(“a=%d,b=%d\n”, a, b); } 输出的结果为: *px=____, *py=____ a=____, b=____ 问下划线的部分应是什么,请完成。 3. 考题三: Exchg2(int &x, int &y) { int tmp=x; x=y; y=tmp; print(“x=%d,y=%d\n”,x,y); } main() { int a=4; int b=6; Exchg2(a,b); Print(“a=%d,b=%d\n”, a, b); } 输出的结果: x=____, y=____ a=____, b=____ 问下划线的部分输出的应是什么,请完成。 你不在机子上试,能作出来吗?你对你写出的答案有多大的把握? 正确的答案,想知道吗?(呵呵,让我慢慢地告诉你吧!) 好,废话少说,继续我们的探索之旅了。 我们都知道:C语言中函数参数的传递有:值传递,地址传递,引用传递这三 种形式。题一为值传递,题二为地址传递,题三为引用传递。不过,正是这几种参数 传递的形式,曾把我给搞得晕头转向。我相信也有很多人与我有同感吧? 下面请让我逐个地谈谈这三种传递形式。 二. 函数参数传递方式之一:值传递 1. 值传递的一个错误认识 先看题一中 Exchg1函数的定义: void Exchg1(int x, int y)//定义中的 x,y变量被称为 Exchg1函数的形式参数 { int tmp; tmp=x; x=y; y=tmp; printf(“x=%d,y=%d\n”,x,y) } 问:你认为这个函数是在做什么呀? 答:好像是对参数 x,y的值对调吧? 请往下看,我想利用这个函数来完成对 a,b两个变量值的对调,程序如下: void main() { int a=4,b=6; Exchg1 (a,b)//a,b变量为 Exchg1函数的实际参数。 /printf(“a=%d,b=%d\n”,a,b) } 我问:Exchg1 ()里头的 printf(“x=%d,y=%d\n”,x,y)语句会输出什么啊? 我再问:Exchg1 ()后的 printf(“a=%d,b=%d\n”,a,b)语句输出的是什么? 程序输出的结果是: x=6 , y=4 a=4 , b=6//为什么不是 a=6,b=4呢? 奇怪,明明我把 a,b分别代入了 x,y中,并在函数里完成了两个变量值的交换,为什么 a,b变量值 还是没有交换(仍然是 a==4,b==6,而不是 a==6,b==4)?如果你也会有这个疑问,那是因为你跟 本就不知实参 a,b与形参 x,y的关系了。 2. 一个预备的常识 为了说明这个问题,我先给出一个代码: int a=4; int x; x=a; x=x+3; 看好了没,现在我问你:最终 a值是多少,x值是多少? (怎么搞的,给我这个小儿科的问题。还不简单,不就是 a==4x==7嘛!) 在这个代码中,你要明白一个东西:虽然 a值赋给了 x,但是 a变量并不是 x 变量哦。我们对 x任何的修改,都不会改变 a变量。呵呵!虽然简单,并且一看就理 所当然,不过可是一个很重要的认识喔。 3. 理解值传递的形式 看调用 Exch1函数的代码: main() { int a=4,b=6; Exchg1(a,b) //这里调用了 Exchg1函数 printf(“a=%d,b=%d”,a,b) } Exchg1(a,b)时所完成的操作代码如下所示。 int x=a;//← int y=b;//←注意这里,头两行是调用函数时的隐含操作 int tmp; tmp=x; x=y; y=tmp; 请注意在调用执行 Exchg1函数的操作中我人为地加上了头两句: int x=a; int y=b; 这是调用函数时的两个隐含动作。它确实存在,现在我只不过把它显式地写了出来而已。问题一 下就清晰起来啦。(看到这里,现在你认为函数里面交换操作的是 a,b变量或者只是 x,y变量呢?) 原来 ,其实函数在调用时是隐含地把实参 a,b 的值分别赋值给了 x,y,之后在你写的 Exchg1函 数体内再也没有对 a,b进行任何的操作了。交换的只是 x,y变量。并不是 a,b。当然 a,b的值没有改变 啦!函数只是把 a,b的值通过赋值传递给了 x,y,函数里头操作的只是 x,y的值并不是 a,b的值。这就 是所谓的参数的值传递了。 哈哈,终于明白了,正是因为它隐含了那两个的赋值操作,才让我们产生了前述的迷惑(以为 a, b已经代替了 x,y,对 x,y的操作就是对 a,b的操作了,这是一个错误的观点啊!)。 第 5篇:函数参数的传递(下) 三. 函数参数传递方式之二:地址传递 继续——地址传递的问题! 看题二的代码: Exchg2(int *px, int *py) { int tmp=*px; *px=*py; *py=tmp; print(“*px=%d,*py=%d\n”,*px,*py); } main() { int a=4; int b=6; Exchg2(&a,&b); Print(“a=%d,b=%d\n”, a, b); } 它的输出结果是: *px=6,*py=4 a=6,b=4 看函数的接口部分:Exchg2(int *px,int *py),请注意:参数 px,py都是指针。 再看调用处:Exchg2(&a, &b); 它将 a的地址(&a)代入到 px,b的地址(&b)代入到 py。同上面的值传递一样, 函数调用时作了两个隐含的操作:将&a,&b的值赋值给了 px,py。 px=&a; py=&b; 呵呵!我们发现,其实它与值传递并没有什么不同,只不过这里是将 a,b的地 址值传递给了 px,py,而不是传递的 a,b的内容,而(请好好地在比较比较啦) 整个 Exchg2函数调用是如下执行的: px=&a;// py=&b;//请注意这两行,它是调用 Exchg2的隐含动作。 int tmp=*px; *px=*py; *py=tmp; print(“*px=%d,*py=%d\n”,*px,*py); 这样,有了头两行的隐含赋值操作。我们现在已经可以看出,指针 px,py的值已经分别是 a,b 变量的地址值了。接下来,对*px,*py的操作当然也就是对 a,b变量本身的操作了。所以函数里头的 交换就是对 a,b值的交换了,这就是所谓的地址传递(传递 a,b的地址给了 px,py),你现在明白了吗? 四. 函数参数传递方式之三:引用传递 看题三的代码: Exchg3(int &x, int &y) //注意定义处的形式参数的格式与值传递不同 { int tmp=x; x=y; y=tmp; print(“x=%d,y=%d\n”,x,y); } main() { int a=4; int b=6; Exchg3(a,b);//注意:这里调用方式与值传递一样 Print(“a=%d,b=%d\n”, a, b); } 输出结果: x=6, y=4 a=6, b=4//这个输出结果与值传递不同。 看到没有,与值传递相比,代码格式上只有一处是不同的,即在定义处: Exchg3(int &x, int &y)。 但是我们发现 a与 b的值发生了对调。这说明了 Exchg3(a,b)里头修改的是 a,b 变量,而不只是修改 x,y了。 我们先看 Exchg3函数的定义处 Exchg3(int &x,int &y)。参数 x,y是 int的变量,调用时我们可以 像值传递(如: Exchg1(a,b); )一样调用函数(如: Exchg3(a,b); )。但是 x,y前都有一个取地址符号&。 有了这个,调用 Exchg3时函数会将 a,b 分别代替了 x,y了,我们称 x,y分别引用了 a,b变量。这样函 数里头操作的其实就是实参 a,b本身了,也就是说函数里是可以直接修改到 a,b的值了。 最后对值传递与引用传递作一个比较: 1. 在函数定义格式上有不同: 值传递在定义处是:Exchg1(int x, int y); 引用传递在这义处是:Exchg1(int &x, int &y); 2. 调用时有相同的格式: 值传递:Exchg1(a,b); 引用传递:Exchg3(a,b); 3. 功能上是不同的: 值传递的函数里操作的不是 a,b变量本身,只是将 a,b值赋给了 x,y函数里操作 的只是 x,y变量而不是 a,b,显示 a,b的值不会被 Exchg1函数所修改。 引用传递 Exchg3(a,b)函数里是用 a,b分别代替了 x,y。函数里操作的就是 a,b 变量的本身,因此 a,b的值可在函数里被修改的。 第 6篇 指向另一指针的指针 一. 回顾指针概念: 早在本系列第二篇中我就对指针的实质进行了阐述。今天我们又要学习一个叫做指向另一指针地 址的指针。让我们先回顾一下指针的概念吧! 当我们程序如下申明变量: short int i; char a; short int * pi; 程序会在内存某地址空间上为各变量开辟空间,如下图所示。 内存地址→6789101112131415 ------------------------------------------------------------------------------------- …|||||||||| ------------------------------------------------------------------------------------- |short int i |char a||short int * pi| 图中所示中可看出: i 变量在内存地址 5的位置,占两个字节。 a变量在内存地址 7的位置,占一个字节。 pi变量在内存地址 9的位置,占两个字节。(注:pi 是指针,我这里指针的宽度 只有两个字节,32位系统是四个字节) 接下来如下赋值: i=50; pi=&i; 经过上在两句的赋值,变量的内存映象如下: 内存地址→6789101112131415 -------------------------------------------------------------------------------------- …|50|||6|||| -------------------------------------------------------------------------------------- |short int i |char a||short int * pi| 看到没有:短整型指针变量 pi的值为 6,它就是 I变量的内存起始地址。所以,这时当我们对*pi 进行读写操作时,其实就是对 i变量的读写操作。如: *pi=5;//就是等价于 I=5; 你可以回看本系列的第二篇,那里有更加详细的解说。 二. 指针的地址与指向另一指针地址的指针 在上一节中,我们看到,指针变量本身与其它变量一样也是在某个内存地址中的,如 pi的内存起 始地址是 10。同样的,我们也可能让某个指针指向这个地址。 看下面代码: short int * * ppi;//这是一个指向指针的指针,注意有两个*号 ppi=π 第一句:short int * * ppi;——申明了一个指针变量 ppi,这个 ppi是用来存储 (或称指向)一个 short int * 类型指针变量的地址。 第二句:&pi那就是取 pi的地址,ppi=π就是把 pi的地址赋给了 ppi。即将地址 值 10赋值给 ppi。如下图: 内存地址→6789101112131415 ------------------------------------------------------------------------------------ …|50|||6|10|| ------------------------------------------------------------------------------------ |short int i|char a||short int * pi|short int ** ppi| 从图中看出,指针变量 ppi的内容就是指针变量 pi的起始地址。于是…… ppi的值是多少呢?——10。 *ppi的值是多少呢?——6,即 pi的值。 **ppi的值是多少呢?——50,即 I的值,也是*pi的值。 呵呵!不用我说太多了,我相信你应明白这种指针了吧! 三. 一个应用实例 1. 设计一个函数:void find1(char array[], char search, char * pi) 要求:这个函数参数中的数组 array是以 0值为结束的字符串,要求在字符串 array中查找字符是 参数 search里的字符。如果找到,函数通过第三个参数(pa)返回值为 array字符串中第一个找到的 字符的地址。如果没找到,则为 pa为 0。 设计:依题意,实现代码如下。 void find1(char [] array, char search, char * pa) { int i; for (i=0;*(array+i)!=0;i++) { if (*(array+i)==search) { pa=array+i break; } else if (*(array+i)==0) { pa=0; break; } } } 你觉得这个函数能实现所要求的功能吗? 调试: 我下面调用这个函数试试。 void main() { char str[]={“afsdfsdfdf\0”};//待查找的字符串 char a=’d’;//设置要查找的字符 char * p=0;//如果查找到后指针 p将指向字符串中查找到的第一个字符的地 址。 find1(str,a,p);//调用函数以实现所要操作。 if (0==p ) { printf (“没找到!\n”);//1.如果没找到则输出此句 } else { printf(“找到了,p=%d”,p);//如果找到则输出此句 } } 分析: 上面代码,你认为会是输出什么呢? 运行试试。 唉!怎么输出的是:没有找到! 而不是:找到了,……。 明明 a值为’d’,而 str字符串的第四个字符是’d’,应该找得到呀! 再看函数定义处:void find1(char [] array, char search, char * pa) 看调用处:find1(str,a,p); 依我在第五篇的分析方法,函数调用时会对每一个参数进行一个隐含的赋值操 作。 整个调用如下: array=str; search=a; pa=p;//请注意:以上三句是调用时隐含的动作。 int i; for (i=0;*(array+i)!=0;i++) { if (*(array+i)==search) { pa=array+i break; } else if (*(array+i)==0) { pa=0; break; } } 哦!参数 pa与参数 search的传递并没有什么不同,都是值传递嘛(小语:地址传递其实就是地址 值传递嘛)!所以对形参变量 pa值(当然值是一个地址值)的修改并不会改变实参变量 p值,因此 p 的值并没有改变(即 p的指向并没有被改变)。 (如果还有疑问,再看一看《第五篇:函数参数的传递》了。) 修正: void find2(char [] array, char search, char ** ppa) { int i; for (i=0;*(array+i)!=0;i++) { if (*(array+i)==search) { *ppa=array+i break; } else if (*(array+i)==0) { *ppa=0; break; } } } 主函数的调用处改如下: find2(str,a,&p);//调用函数以实现所要操作。 再分析: 这样调用函数时的整个操作变成如下: array=str; search=a; ppa=&p;//请注意:以上三句是调用时隐含的动作。 int i; for (i=0;*(array+i)!=0;i++) { if (*(array+i)==search) { *ppa=array+i break; } else if (*(array+i)==0) { *ppa=0; break; } } 看明白了吗? ppa指向指针 p的地址。 对*ppa的修改就是对 p值的修改。 你自行去调试。 经过修改后的程序就可以完成所要的功能了。 看懂了这个例子,也就达到了本篇所要求的目的。 第 7篇 函数名与函数指针 一 通常的函数调用 一个通常的函数调用的例子: //自行包含头文件 void MyFun(int x);//此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun(10);//这里是调用 MyFun(10);函数 return 0; } void MyFun(int x)//这里定义一个 MyFun函数 { printf(“%d\n”,x); } 这个 MyFun函数是一个无返回值的函数,它并不完成什么事情。这种调用函数的格式你应该是很 熟悉的吧!看主函数中调用 MyFun函数的书写格式: MyFun(10); 我们一开始只是从功能上或者说从数学意义上理解 MyFun这个函数,知道 MyFun函数名代表的是 一个功能(或是说一段代码)。 直到—— 学习到函数指针概念时。我才不得不在思考:函数名到底又是什么东西呢? (不要以为这是没有什么意义的事噢!呵呵,继续往下看你就知道了。) 二 函数指针变量的申明 就象某一数据变量的内存地址可以存储在相应的指针变量中一样,函数的首地址也以存储在某个 函数指针变量里的。这样,我就可以通过这个函数指针变量来调用所指向的函数了。 在 C系列语言中,任何一个变量,总是要先申明,之后才能使用的。那么,函数指针变量也应该 要先申明吧?那又是如何来申明呢?以上面的例子为例,我来申明一个可以指向 MyFun函数的函数指 针变量 FunP。下面就是申明 FunP变量的方法: void (*FunP)(int) ;//也可写成 void (*FunP)(int x); 你看,整个函数指针变量的申明格式如同函数 MyFun的申明处一样,只不过——我们把 MyFun改 成(*FunP)而已,这样就有了一个能指向 MyFun函数的指针 FunP了。(当然,这个 FunP指针变量 也可以指向所有其它具有相同参数及返回值的函数了。) 三 通过函数指针变量调用函数 有了 FunP指针变量后,我们就可以对它赋值指向 MyFun,然后通过 FunP来调用 MyFun函数了。 看我如何通过 FunP指针变量来调用 MyFun函数的: //自行包含头文件 void MyFun(int x);//这个申明也可写成:void MyFun( int ); void (*FunP)(int );//也可申明成 void(*FunP)(int x),但习惯上一般不这样。 int main(int argc, char* argv[]) { MyFun(10);//这是直接调用 MyFun函数 FunP=&MyFun;//将 MyFun函数的地址赋给 FunP变量 (*FunP)(20);//这是通过函数指针变量 FunP来调用 MyFun函数的。 } void MyFun(int x)//这里定义一个 MyFun函数 { printf(“%d\n”,x); } 请看黑体字部分的代码及注释。 运行看看。嗯,不错,程序运行得很好。 哦,我的感觉是:MyFun与 FunP的类型关系类似于 int 与 int *的关系。函数 MyFun好像是一个如 int的变量(或常量),而 FunP则像一个如 int *一样的指针变 量。 int i,*pi; pi=&i;//与 FunP=&MyFun比较。 (你的感觉呢?) 呵呵,其实不然—— 四 调用函数的其它书写格式 函数指针也可如下使用,来完成同样的事情: //自行包含头文件 void MyFun(int x); void (*FunP)(int );//申明一个用以指向同样参数,返回值函数的指针变量。 int main(int argc, char* argv[]) { MyFun(10);//这里是调用 MyFun(10);函数 FunP=MyFun;//将 MyFun函数的地址赋给 FunP变量 FunP(20);//这是通过函数指针变量来调用 MyFun函数的。 return 0; } void MyFun(int x)//这里定义一个 MyFun函数 { printf(“%d\n”,x); } 我改了黑体字部分(请自行与之前的代码比较一下)。 运行试试,啊!一样地成功。 咦? FunP=MyFun; 可以这样将 MyFun值同赋值给 FunP,难道 MyFun与 FunP是同一数据类型(即如同的 int 与 int 的关系),而不是如同 int 与 int*的关系了?(有没有一点点的糊涂了?) 看来与之前的代码有点矛盾了,是吧!所以我说嘛! 请容许我暂不给你解释,继续看以下几种情况(这些可都是可以正确运行的代码哟!): 代码之三: int main(int argc, char* argv[]) { MyFun(10);//这里是调用 MyFun(10);函数 FunP=&MyFun;//将 MyFun函数的地址赋给 FunP变量 FunP(20);//这是通过函数指针变量来调用 MyFun函数的。 return 0; } 代码之四: int main(int argc, char* argv[]) { MyFun(10);//这里是调用 MyFun(10);函数 FunP=MyFun;//将 MyFun函数的地址赋给 FunP变量 (*FunP)(20);//这是通过函数指针变量来调用 MyFun函数的。 return 0; } 真的是可以这样的噢! (哇!真是要晕倒了!) 还有呐!看—— int main(int argc, char* argv[]) { (*MyFun)(10);//看,函数名 MyFun也可以有这样的调用格式 return 0; } 你也许第一次见到吧:函数名调用也可以是这样写的啊!(只不过我们平常没有这样书写罢了。) 那么,这些又说明了什么呢? 呵呵!依据以往的知识和经验来推理本篇的“新发现”,我想就连“福尔摩斯”也必定会由此分析并推 断出以下的结论: 1. 其实,MyFun的函数名与 FunP函数指针都是一样的,即都是函数指针。MyFun函数名是一个 函数指针常量,而 FunP是一个函数数指针变量,这是它们的关系。 2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以 C语言的设计者们才会设计成又可允许 MyFun(10);这种形式地调用(这样方便多了并与数学中的函数 形式一样,不是吗?)。 3. 为统一起见,FunP函数指针变量也可以 FunP(10)的形式来调用。 4. 赋值时,即可 FunP=&MyFun形式,也可 FunP=MyFun。 上述代码的写法,随便你爱怎么着! 请这样理解吧!这可是有助于你对函数指针的应用喽! 最后—— 补充说明一点:在函数的申明处: void MyFun(int );//不能写成 void (*MyFun)(int )。 void (*FunP)(int );//不能写成 void FunP(int )。 (请看注释)这一点是要注意的。 五 定义某一函数的指针类型: 就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数 指针变量。 我先给你一个自定义数据类型的例子。 typedef int* PINT;//为 int* 类型定义了一个 PINT的别名 int main() { int x; PINT px=&x;//与 int * px=&x;是等价的。PINT类型其实就是 int * 类型 *px=10;//px就是 int*类型的变量 return 0; } 根据注释,应该不难看懂吧!(虽然你可能很少这样定义使用,但以后学习Wi n32编程时会经常见到的。) 下面我们来看一下函数指针类型的定义及使用:(请与上对照!) //自行包含头文件 void MyFun(int x);//此处的申明也可写成:void MyFun( int ); typedef void (*FunType)(int );//这样只是定义一个函数指针类型 FunType FunP;//然后用 FunType类型来申明全局 FunP变量 int main(int argc, char* argv[]) { //FunType FunP;//函数指针变量当然也是可以是局部的 ,那就请在这里申明 了。 MyFun(10); FunP=&MyFun; (*FunP)(20); return 0; } void MyFun(int x) { printf(“%d\n”,x); } 看黑体部分: 首先,在 void (*FunType)(int ); 前加了一个 typedef 。这样只是定义一个名 为 FunType函数指针类型,而不是一个 FunType变量。 然后,FunType FunP;这句就如 PINT px;一样地申明一个 FunP变量。 其它相同。整个程序完成了相同的事。 这样做法的好处是: 有了 FunType类型后,我们就可以同样地、很方便地用 FunType类型来申明多 个同类型的函数指针变量了。如下: FunType FunP2; FunType FunP3; //…… 六 函数指针作为某个函数的参数 既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。所以,你还应知道函 数指针是如何作为某个函数的参数来传递使用的。 给你一个实例: 要求:我要设计一个 CallMyFun函数,这个函数可以通过参数中的函数指针值 不同来分别调用 MyFun1、MyFun2、MyFun3这三个函数(注:这三个函数的定义格 式应相同)。 实现:代码如下: //自行包含头文件 void MyFun1(int x); void MyFun2(int x); void MyFun3(int x); typedef void (*FunType)(int ); //②. 定义一个函数指针类型 FunType,与 ①函数类型一至 void CallMyFun(FunType fp,int x); int main(int argc, char* argv[]) { CallMyFun(MyFun1,10);//⑤. 通过 CallMyFun函数分别调用三个不同的函 数 CallMyFun(MyFun2,20); CallMyFun(MyFun3,30); } void CallMyFun(FunType fp,int x) //③. 参数 fp的类型是 FunType。 { fp(x);//④. 通过 fp的指针执行传递进来的函数,注意 fp所指的函数是有一个 参数的 } void MyFun1(int x) // ①. 这是个有一个参数的函数,以下两个函数也相同 { printf(“函数 MyFun1中输出:%d\n”,x); } void MyFun2(int x) { printf(“函数 MyFun2中输出:%d\n”,x); } void MyFun3(int x) { printf(“函数 MyFun3中输出:%d\n”,x); }
本文档为【彻底搞定C指针】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_546748
暂无简介~
格式:pdf
大小:234KB
软件:PDF阅读器
页数:25
分类:互联网
上传时间:2010-01-19
浏览量:6