C语言-函数指针与函数名的区别***

 

        记得大学时老师曾说函数的函数名是函数的入口的指针,之前看block通过clang编译生成的C代码发现很多函数指针,于是想了解函数指针与函数名有什么区别?以及函数指针一般都有些什么作用。

 

函数指针与函数名的区别

首先先定义一函数以及一个指向盖函数的函数指针,并分别对他们进行调用。

 

//VS 2017
void fun(int& x);
//定义函数fun  
void fun(int& x) {
	x++;
	printf("%i\n", x);
}

typedef void(*FunP)(int& n);
void  fnt(FunP fn, int& n)
{
	fn(n);
}

int main()
{
#if 1
    void(*funPattr[])(int&);//声明函数指针数组funPattr
	void(*funP)(int&);//声明函数指针funP  
	funP = fun;

	int x = 0;
	printf("&fun=%p\n", &fun);
	printf("fun=%p\n", fun);
	printf("*fun=%p\n", *fun);
	(&fun)(x);
	fun(x);
	(*fun)(x);

	printf("&funP=%p\n", &funP);
	printf("funP=%p\n", funP);
	printf("*funP=%p\n", *funP);
	//(&funP)(x);
	funP(x);
	(*funP)(x);

	fnt(fun, x);
	fnt(&fun, x);

	fnt(funP, x);

#endif

	system("pause");
	return 0;
}

输出结果为

    正如我们平常使用的那样,1如何2正常输出了。为了确认函数名是否等价于函数指针,于是把函数名以及函数指针的调用方式换了一下。发现(* <函数名>)()的的形式也能进行调用并正常输出,而函数指针直接调用也没有问题。

    这里暂时得出两个结论

    1、函数名的使用基本等价于函数指针,函数名、取地址&、取内容*得到的都是函数的地址

    2、函数名也可以(* <函数名>)()来调用,只是这种方法读写都不方便,所以被简化了

    得到一个问题是:为什么使用“funP = &fun”的形式对funP赋值,而不直接使用“funP = fun”

对于上面得出的问题,我们试着直接输出funP与fun作为指针的值,进行比较。

       首先,所有结果都正常打印了(似乎fun真的像一个指针)。

       其次fun是一个指向自己的指针。根据大家常说的fun作为函数的入口的依据,那么它的地址就是函数入口的地址,其次我们通过fun来找到函数,那么他就应该指向函数的入口。这么解释似乎能说的通为什么fun是一个指向自己的指针。虽然感觉有点怪怪的。

       经过上面两端代码后我们好像能确定函数名就是函数指针,那我们看看作为函数指针,它能否做其他函数指针能做的事,比如赋值。

第一种:函数名与FunP函数指针都是函数指针。fun是一个函数指针常量,funP是一个函数数指针变量。

        虽然通过常量与变量来解释函数名无法赋值可以帮助理解,但是我们发现对fun赋值时编译器给的错误提示并不是说对常量进行赋值,而是告诉我们=号两端格式不匹配。对此,第二种理解更合理。

 

第二种:函数名和数组名实际上都不是指针,但是,在使用时可以退化成指针,即编译器可以帮助我们实现自动的转换。

        这也可以解释为什么当我们在=号右侧使用函数名时,无论是取值还是取地址都没有问题,因为编译替我们做了相当于强制类型转换的工作,而在当函数名在=号左侧时,右侧的函数指针并没有这个功能,毕竟他们俩不是同一种结构。

函数指针的作用

           当我无法区别函数名与函数指针时,我很好奇既然函数名也是函数指针类型,那为什么不直接使用函数名。提出函数指针的目的是什么,它有什么作用。虽然现在明白了函数名不等于函数指针,但是问题还是要解决。

1、作为变量传递,可称为参数

       既然函数指针如同别的指针变量一样通过*来获得,那么函数指针作为变量,自然可以进行赋值,取值操作,可以作为函数的参数进行传递。普通指针变量能做什么它就能做什么。

2、优化函数调用,封装

      通常函数名的命名都是见名知意,直接用函数名调用可读性自然要好,但如果是不想给别人查看的代码,被人破解后通过函数名直接就了解具体函数作用与函数调用,而函数指针可以作为函数的一层外衣,提供一定的保护作用。

      其次通过函数指针来调用函数,可以起到一定的封装效果,函数指针作为引用层(中层),函数作为实现层(底层),便于分层设计,函数指针可以为上层用户提供统一借口,便于系统抽象各个功能或操作,降低程序耦合度。如

fopen就是个例子,他可以打开文件。而C里面将磁盘文件、串口、USB等诸多设备抽象为文件。
为C++实现多态性的虚函数表也是通过函数指针实现。
3、回调函数
  这是最重要的使用场景了,回调函数就是一个通过函数指针调用的函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,
用于对该事件或条件进行响应。举个简单的例子。
如去干洗店洗衣服,我们通常会留下电话号码,而干洗店洗好衣服后就会通过电话通知我们,
让我们来取衣服。这个场景里电话号码就是回调函数,相当于函数指针。
这在实现通知机制的时候就能看到。其次在我们编写一个对一般数据类型进行操作的库时,
为了能让库可用于多种数据类型(int、float、string),也可以使用函数指针,并进行回调。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

隨意的風

如果你觉得有帮助,期待你的打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值