文章目录
- 指针函数
- 函数指针
- 函数指针的定义与指针函数的声明的区别
- 函数指针的定义
- 指针函数的声明
- typedef在函数指针方面的使用
- typedef和using 给函数指针的类型取别名
- typedef和using 给函数的类型取别名
指针函数
指针函数:
也叫指针型函数,本质上就是一个函数,是一种特殊的函数类型,它返回一个指针作为函数的返回值。
指针型函数声明的格式:
返回类型* 函数名(参数列表);
或者
返回类型 *函数名(参数列表);
推荐使用第一种声明方式。更清晰明了
。
其中,返回类型是指针类型,函数名是指针型函数的名称,参数列表是指针函数的参数列表。
例如:
普通函数(即返回值不是地址)
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* 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;
}