函数指针和指针函数的讲解

本文详细解释了指针函数的定义、声明、返回地址的概念,以及typedef在处理函数指针类型别名中的运用,包括函数指针变量和函数类型的不同。通过实例演示了如何使用typedef和using创建函数指针类型别名以及调用函数。
摘要由CSDN通过智能技术生成

指针函数

指针函数:
也叫指针型函数,本质上就是一个函数,是一种特殊的函数类型,它返回一个指针作为函数的返回值。
指针型函数声明的格式:
返回类型* 函数名(参数列表);
或者
返回类型 *函数名(参数列表);
推荐使用第一种声明方式。更清晰明了
其中,返回类型是指针类型,函数名是指针型函数的名称,参数列表是指针函数的参数列表。

例如:
普通函数(即返回值不是地址)

int fun0(int);//声明了一个返回值类型为int,函数参数为int型的函数。

指针函数(即返回值是地址)

int* fun1 (int);//声明了一个返回值为整型的指针,函数参数为int,int的指针型函数。
void* fun2 (int, int);//声明了一个返回值为void型的指针,函数参数为int,int的指针型函数。

注意: 指针函数一定有返回值,形如 void* 的返回值类型,为任意指针类型。而不是无返回值。不管返回的是 int* 还是 void* 建议都判断是否为空指针。

例子:

int* func(int a, int b);//指针型函数声明; 或者为 int* func(int, int);即声明里的形参可以只写类型,不写形参变量。这也是推荐的写法。
	
int* func(int a, int b) //指针型函数的定义
{
   int* p = (int*)malloc(sizeof(int));//在堆内存中申请了一块4个字节的内存,并把指向这块堆内存的地址返回,赋值给指针变量p
   *p = a + b;//将a+b的和,赋值给指针变量p所指向的内存空间。注:当*p作为左值时,表示的就是它所指向的那块内存对应的变量。
   return p;
}

注意:和普通函数一样,指针函数没有形参时,声明时后面的括号()也是不能省略的,否则就变成 指针变量的定义了。

指针函数就是一个普通的函数,普通到仅仅是因为它的函数返回值是地址值而已。这与其他一般函数唯一的区别
就是在函数名前面多了一个*号,而这个函数就是一个指针函数。
一句话总结:返回的是地址值,在指针函数被调用时用指针变量来接收此指针函数返回的地址值。

函数指针

函数指针是指向函数的指针变量,即本质是一个指针变量,指针的类型为函数类型,即函数型指针。因此“函数指针”本身首先应是指针变量。
只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。C/C++在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是大体一致的,总之,函数型指针 指向 函数的地址 ,类似于 int型指针,指向int型数据的内存地址。函数指针有两个用途:调用函数和做函数的参数。

先看一个int类型的指针的声明,然后和函数型指针做对比:

int* p;

声明一个函数型(假如是一个有两个int类型的参数以及一个 int类型的返回值)的指针:

int (*fun_ptr) (int, int);//定义 一个【有两个int类型的参数以及一个int类型的返回值】类型 的函数指针。fun_ptr 就是指针变量名

故此指针可以用来指向:符合 一个有两个int类型的参数以及一个 int类型的返回值的任意函数。
在这里插入图片描述二者不同的只是 函数指针多了一个参数列表
例子:

#include <iostream>
#include <stdio.h>

void Fun(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;
};

int Fun1(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;// a = 2, b = 3
    return 0;
};

int main(int argc, char *argv[])
{

   void (*fun_ptr) (int, int);//定义一个函数指针 

    fun_ptr = Fun;//函数名其实就是函数的地址,函数可以看成和数组名一样都是一个指针常量,即指针的指向地址不可改变
   // fun_ptr = Fun1;//编译报错,函数指针(fun_ptr)的类型 和 Fun1函数的类型不一致。必须要保证返回值和参数列表一样。否则函数指针不能指向此函数地址

    // std::cout << "Fun函数地址:" << Fun <<std::endl;//这样方式打印函数地址会把函数指针转换为bool类型,故打印出来的是总是1,改用下面的printf函数
    fun_ptr(2,3);//使用函数指针来调用函数
    printf("Fun函数的地址为:%p\n",Fun);//Fun函数的地址为:0x400910
    printf("Fun1函数的地址为:%p\n",Fun1);//Fun1函数的地址为:0x400922
    printf("fun_ptr的指向地址:%p\n",fun_ptr);//fun_ptr的指向地址:0x400910
}

函数指针的定义与指针函数的声明的区别

函数指针的定义

void (*fun) (int, int);//fun 是一个有两个int类型的参数以及一个 int类型的返回值的函数指针。//指针就是变量。变量名是fun.

注意:这里提指针和地址的区别,指针是变量,它是用来存储地址 ,而地址是一个常量值。类似于,变量和变量的值之间的关系。

指针函数的声明

void* fun (int, int);//fun是返回一个指针的函数

注意: 定义函数指针时,第一个()是不能省略的,如果省略的话,就会导致完全不同的情况了。就变成 指针函数的声明了。

typedef在函数指针方面的使用

typedef 操作符是在C/C++中经常使用的。它不仅可以给简单类型 取别名。还可以给复杂类型 取别名。
格式如下:
typedef 类型 别名;
在C++11 中又引入了using的另一个作用 来给类型 取别名。using这个作用和typedef的作用是一样的,二者等价,在某些复杂类型上使用有using 比用typedef更容易直观理解。
格式如下:
using 别名 = 类型;
注意:using之前已有的作用是 引入命名空间 如引入标准库的命名空间: using namespace std;

typedef和using 给函数指针的类型取别名

typedef 在给 函数指针类型 取别名上的应用(这也是typedef 在给复杂类型 取别名上的应用):

typedef void (*FUN) (int, int);//这里的FUN是一种 void (*) (int, int) 类型的别名,而不是上面的函数指针,而是一个类型。可以FUN来表示 函数指针 的类型。

注意: 也许这样 写 typedef void (*) (int, int) FUN; 更符合 “typedef 类型 别名;”的格式。但是这样写会编译报错,也就是这种写法是错误的。
也可以用using 来取别名(C++11中开始支持)

	using FUN = void (*) (int, int);//感觉这样方式更符合理解。这也是为啥C++11中开始支持使用using来给类型取别名的意义。

注意:不管 typedef 还是using 都是给已有的简单类型 或者 复杂类型 取一个别名,而不是创建一个新的类型。

例子:

#include <iostream>
#include <stdio.h>

typedef void (*FUNC) (int, int);//使用typedef来取别名
using FUNC1 = void (*) (int, int);//使用using来取别名
using FUNC2 = int (*) (int, int);//使用using来取别名

void Fun(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;
};

int Fun1(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;
    return 0;
};

int main(int argc, char *argv[])
{

    FUNC fun_ptr = Fun; //比直接 void (*fun_ptr) (int, int) = Fun;写简洁
    FUNC1 fun_ptr1 = Fun; //比直接 void (*fun_ptr1) (int, int) = Fun;写简洁
    FUNC2 fun_ptr2 = Fun1; //比直接 int (*fun_ptr2) (int, int) = Fun1;写简洁

     // std::cout << "Fun函数地址:" << Fun <<std::endl;//这样方式打印函数地址会把函数指针转换为bool类型,故打印出来的是总是1,改用下面的printf函数
    fun_ptr(2,3);//使用函数指针来调用函数  // a = 2, b = 3
    fun_ptr1(5,3);//使用函数指针来调用函数 // a = 5, b = 3
    fun_ptr2(6,3);//使用函数指针来调用函数 // a = 6, b = 3
    printf("Fun函数的地址为:%p\n",Fun);//Fun函数的地址为:0x400910
    printf("Fun1函数的地址为:%p\n",Fun1);//Fun1函数的地址为:0x400922
    printf("fun_ptr的指向地址:%p\n",fun_ptr);//fun_ptr的指向地址:0x400910
    printf("fun_ptr1的指向地址:%p\n",fun_ptr1);//fun_ptr1的指向地址:0x400910
    printf("fun_ptr2的指向地址:%p\n",fun_ptr2);//fun_ptr2的指向地址:0x400922
    return 0;
}

typedef和using 给函数的类型取别名

也可以给函数类型取别名

typedef void FUNC4(int, int);//使用typedef来给函数的类型取别名,不看 前面的typedef 单独看 void FUNC4(int, int);是一个函数声明,即函数原型。

和函数指针取别名进行比较:

typedef void (*FUNC) (int, int);//使用typedef来给函数指针的类型取别名
分析:
//void (*FUNC) (int, int); //定义一个 返回值为void,拥有两个int参数类型的 函数指针,函数指针名称为FUNC
//void FUNC4(int, int); //声明一个 返回值为void,拥有两个int参数类型的 函数,此函数的函数名是 FUNC4.
//它们就是一个表示 函数指针,一个表示函数
//同理 形如上面的 两者前面都加上typedef后,FUNC表示的函数指针的类型别名。 FUNC4表示的函数的类型的别名。

完整代码演示:

#include <iostream>
#include <stdio.h>

typedef void (*FUNC) (int, int);//使用typedef来给函数指针的类型取别名
typedef void FUNC4(int, int);//使用typedef来给函数的类型取别名,不看 前面的typedef 单独看 void FUNC4(int, int);是一个函数声明,即函数原型。
//或者使用using
//using FUNC = void (*) (int,int); //等号右侧表示函数指针的类型
//using FUNC4 = void (int,int); //等号右侧表示函数的类型
//两个类型的区别就是一个有*,一个没有*


//void (*FUNC) (int, int); //定义一个 返回值为void,拥有两个int参数类型的 函数指针,函数指针名称为FUNC
//void FUNC4(int, int); //声明一个 返回值为void,拥有两个int参数类型的 函数,此函数的函数名是 FUNC4.
//它们就是一个表示 函数指针,一个表示函数
//同理 两者前面都加上typedef后,FUNC表示的函数指针的类型别名。 FUNC4表示的函数类型的别名。

void Fun(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;
};

int Fun1(int a, int b){//函数定义
    std::cout << "a =" << a <<", b=" << b <<std::endl;
    return 0;
};

int main(int argc, char *argv[])
{
    FUNC fun_ptr = Fun; //比直接 void (*fun_ptr) (int, int) = Fun;简洁
    FUNC4* fun_ptr3 = Fun;// 在FUNC4后加* 表示的就是函数指针的类型。可以这样理解 FUNC4是函数的类型 FUNC4*就是指向 和FUNC4相同类型的函数地址 的指针的类型。
    //(void (int,int))* fun_ptr3 = Fun; //直接这样写语义会报错,这也就是为啥用typedef来给复杂类型取别名的用处,至于编译解析交给编译器完成。

    // std::cout << "Fun函数地址:" << Fun <<std::endl;//这样方式打印函数地址会把函数指针转换为bool类型,故打印出来的是总是1,改用下面的printf函数
    fun_ptr(2,3);//使用函数指针来调用函数  // a = 2, b = 3
    fun_ptr3(5,3);//使用函数指针来调用函数 // a =5, b=3
    printf("Fun函数的地址为:%p\n",Fun);//Fun函数的地址为:0x400910
    printf("fun_ptr3的指向地址:%p\n",fun_ptr3);//fun_ptr的指向地址:0x400910
    return 0;
}
  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值