1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 【C语言学习笔记】26. 指针(3)指向指针的指针 传递指针给函数

【C语言学习笔记】26. 指针(3)指向指针的指针 传递指针给函数

时间:2023-10-01 09:29:35

相关推荐

【C语言学习笔记】26. 指针(3)指向指针的指针 传递指针给函数

前言

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

指向指针的指针

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。

一个指向指针的指针变量必须如下声明,即在变量名前放置两个星号。例如,下面声明了一个指向 int 类型指针的指针:

int **var;

当一个目标值被一个指针间接指向到另一个指针时,访问这个值需要使用两个星号运算符,如下面实例所示:

#include <stdio.h>int main (){int V;int *Pt1;int **Pt2;V = 100;/* 获取 V 的地址 */Pt1 = &V;/* 使用运算符 & 获取 Pt1 的地址 */Pt2 = &Pt1;/* 使用 pptr 获取值 */printf("var = %d\n", V );printf("Pt1 = %p\n", Pt1 );printf("*Pt1 = %d\n", *Pt1 );printf("Pt2 = %p\n", Pt2 );printf("**Pt2 = %d\n", **Pt2);return 0;}

当上面的代码被编译和执行时,它会产生下列结果:

var = 100

Pt1 = 0x7ffee2d5e8d8

*Pt1 = 100

Pt2 = 0x7ffee2d5e8d0

**Pt2 = 100

传递指针给函数

C 语言允许您传递指针给函数,只需要简单地声明函数参数为指针类型即可。

下面的实例中,我们传递一个无符号的 long 型指针给函数,并在函数内改变这个值:

#include <stdio.h>#include <time.h>void getSeconds(unsigned long *par);int main (){unsigned long sec;getSeconds( &sec );/* 输出实际值 */printf("Number of seconds: %ld\n", sec );return 0;}void getSeconds(unsigned long *par){/* 获取当前的秒数 */*par = time( NULL );return;}

当上面的代码被编译和执行时,它会产生下列结果:

Number of seconds :1294450468

指针和函数的关系

1、函数指针(指向函数的指针)

一个函数在编译之后,会占据一部分内存,而它的函数名,就是这段函数的首地址。

可以把一个指针声明成为一个指向函数的指针。

C 语言规定函数名会被转换为指向这个函数的指针,除非这个函数名作为 & 操作符或 sizeof 操作符的操作数(注意:函数名用于 sizeof 的操作数是非法的)。也就是说 f = test; 中 test 被自动转换为 &test,而 f = &test; 中已经显示使用了 &test,所以 test 就不会再发生转换了。因此直接引用函数名等效于在函数名上应用 & 运算符,两种方法都会得到指向该函数的指针。

指向函数的指针必须初始化,或者具有 0 值,才能在函数调用中使用。

与数组一样:

(1)禁止对指向函数的指针进行自增运算++

(2)禁止对函数名赋值,函数名也不能用于进行算术运算。

示例1:

int fun1(int,int);int fun1(int a, int b){return a+b;}int main(){int (*pfun1)(int,int); pfun1=fun1;//这里&fun1和fun1的值和类型都一样,用哪个无所谓 int a=(*pfun1)(5,7); //通过函数指针调用函数。}

示例2:

#include <stdio.h>#include <stdlib.h>int Max(int x, int y) //定义Max函数{int z;if (x > y) {z = x;}else {z = y;}return z;}int main() {//定义一个函数指针int(*p)(int, int);int a, b, c;//把函数Max赋给指针变量p, 使p指向Max函数p = Max;printf("please enter a and b:");scanf("%d%d", &a, &b);//通过函数指针调用Max函数c = (*p)(a, b);printf("a = %d\nb = %d\nmax = %d\n", a, b, c);system("pause");return 0;}​

示例2:

#include <stdio.h>​ ​void test( )​{​printf("test called!/n");​}​ ​int main( )​{​ void (*f) ( );​ f = test; ​ f ( );​ (*f)( );​ //test++; // error,标准禁止对指向函数的指针进行自增运算​//test = test + 2; // error,不能对函数名赋值,函数名也不能用于进行算术运算​printf("%p/n", test);​printf("%p/n", &test);​printf("%p/n", *test);​ return 0;​}

运行结果为:

test called!

​test called!

​004013EE​004013EE​004013EE

这里的玄学就是 *test 为什么能和上面两个之前介绍过的输出一样的值。

首先来看函数名 test,是一个符号用来标识一个函数的入口地址,在使用中函数名会被转换为指向这个函数的指针,指针的值就是函数的入口地址,&test 在前面已经说了:显示获取函数的地址。*test 可以认为由于 test 已经被转换成了函数指针, 指向这个函数,所以 *test 就是取这个指针所指向的函数名,而又根据函数名会被转换指向该函数的指针的规则,这个函数也转变成了一个指针,所以 *test 最终也是一个指向函数 test 的指针。也就是说:*test --> *(&test) --> test --> &test。

上述关系十分重要!

为了更加明确,把示例 1 做补充:

#include <stdio.h>int fun1(int,int);int fun1(int a, int b){return a+b;}/* 要调用上面定义函数的主函数 */int main (){int (*pfun1)(int,int);pfun1=fun1;//这里&fun1和fun1的值和类型都一样,用哪个无所谓int a=(*pfun1)(5,7); //通过函数指针调用函数。printf("%d\n",a);int e = fun1(5,7);printf("%d\n",d)int b = (&fun1)(5,7);printf("%d\n",b);int c = (*fun1)(5,7);printf("%d",c);return 0;}//根据关系 *fun1==*&fun1==fun1==&fun1 可知,以上的运行结果会得到4个5+7。//因此在下面的函数指针数组实例中,action[2]()就相当于这里的(&fun1(5,7)),这点务必搞清楚。

2、指针函数(返回值为指针的函数)

所谓指针函数,就是返回指针的函数。在前面笔记中“从函数返回数组”中已经介绍。

C 语言的库函数中有很多都是指针函数,比如字符串处理函数,下面给出一些函数原型:

char *strcat( char *dest, const char *src );​

char *strcpy( char *dest, const char *src );

​char *strchr( const char *s, int c );​

char *strstr( const char *src, const char *sub );

3、两者混用(不常用)

注意函数的返回值不仅仅局限于指向变量的指针,也可以是指向函数的指针。

首先来看这个声明:*int (*function(int)) (double*, char);要了解此声明的含义,首先来看 function(int),将 function 声明为一个函数,它带有一个 int 型的形式参数,这个函数的返回值为一个指针,正是函数指针 int () (double, char); 这个指针指向一个函数,此函数返回 int 型并带有两个分别是 double* 型和 char 型的形参。

如果使用typedef可以将这个声明简化:(没看懂。。。。之后的结构体再补充)

typedef int (*ptf) (double*, char);​

ptf function( int );

另一个例子:

void (*signal (int sig, void (*func) (int siga)) ) ( int siga );

现在要分析的是 signal,因为紧邻 signal 的是优先级最高的括号,首先与括号结合,所以 signal 为一个函数,括号内为 signal 的两个形参,一个为int型,一个为指向函数的指针。接下来从向左看,* 表示指向某对象的指针,它所处的位置表明它是 signal 的返回值类型,现在可以把已经分析过的 signal 整体去掉,得到 void (*) ( int siga )。又是一个函数指针,这个指针与 signal 形参表中的第二个参数类型一样,都是指向接受一个 int 型形参且不返回任何值的函数的指针。

用 typedef 可以将这个声明简化:

typedef int (*p_sig) (double*, char);​

p_sig signal(int sig, p_sig func);

这个 signal 函数是 C 语言的库函数,在 signal.h 中定义,用来处理系统中产生的信号。

4、函数指针数组

假设现在有一个文件处理程序,通过一个菜单按钮来选择相应的操作(打开文件,读文件,写文件,关闭文件)。这些操作都实现为函数且类型相同,分别为:

void open();

void read();

void write();

void close();

现在定义一个函数指针类型的别名PF:

typedef void (*PF) ( );

把以上 4 种操作取地址放入一个数组中,得到:

PF file_options[ ] = {

&open,

&read,

&write,

&close

};

如果不使用 typedef,那么分析起来就会比较复杂,结果是 void (*file_options[ ]) ( );

这个数组中的元素都是指向不接受参数且不返回任何值的函数的指针,因此这是一个函数指针数组。接下来,定义一个函数指针类型的指针action并初始化为函数指针数组的第一个元素:PF* action = file_options;,如果不好理解,可以类比一下:

int ia[4] = {0, 1, 2, 3};

int *ip = ia;,

这里 PF 相当于 int,这样应该比较好懂了。

复习:

int ia[4] = {0, 1, 2, 3};int *ip = ia; //ia就是&ia[0],因此ip指向ia[0]。与此同时ip[1]的含义又和*(ip+1)一样。printf("%p\n",ip);printf("%p\n",ip+1); printf("%d\n",ip[1]); printf("%d\n",*(ip+1));​/*输出结果0x7ffee4cca9b00x7ffee4cca9b411*/

通过对指针 action 进行下标操作可以调用数组中的任一操作,如:action2 会调用 write 操作,以此类推。在实际中,指针 action 可以和鼠标或者其他 GUI 对象相关联,以达到相应的目的。

5、函数与指针的复杂声明(不做要求,一般用 typedef 代替它)

只举一个例子:

int *(*(*fp)(int)) [10];

阅读步骤:

1.从未定义的变量名开始阅读 -------------------------------------------- fp2.往右看,什么也没有,遇到了),因此往左看,遇到一个* ------ 一个指向某对象的指针3.跳出括号,遇到了(int) ----------------------------------- 一个带一个int参数的函数4.向左看,发现一个* --------------------------------------- (函数)返回一个指向某对象的指针5.跳出括号,向右看,遇到[10] ------------------------------ 一个10元素的数组6.向左看,发现一个* --------------------------------------- 一个指向某对象指针7.向左看,发现int ----------------------------------------- int类型

所以 fp 是指向函数的指针(函数指针), 该函数返回一个指向数组的指针,此数组有 10 个 int* 型的元素。

int *(*(*fp)(int)) [10]; (*fp)(int)是一个指向函数的指针ptr*(*fp)(int)相当于一个指针ptr1(指针函数的返回值)最后剩下int *ptr1[10],可以理解。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。