【C语言入门级教】函数指针变量
文章目录
- 1.函数指针变量
- 1.1 函数指针变量的创建
- 1.2 函数指针变量的使⽤通过函数指针调⽤指针指向的函数。
- 1.3 两段有趣的代码
- 1.3.1 typedef关键字
- 2. 函数指针数组
- 3.转移表
1.函数指针变量
1.1 函数指针变量的创建
什么是函数指针变量?
根据整型指针,数组指针,可以类⽐得出结论: 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
那么函数是否有地址呢? 我们做个测试:
#include <stdio.h>
void test()
{ printf("hehe\n");
}
int main()
{ printf("test: %p\n", test); printf("&test: %p\n", &test); //两种写法都是函数的地址,建议使用这种,因为能帮助我们理解return 0;
}
输出结果如下:
test: 005913CA
&test: 005913CA
确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过&函数名的⽅式获得函数的地址。
如果我们要将函数的地址存放起来,就得创建函数指针变量,函数指针变量的写法其实和数组指针⾮常类似。如下:
void test()
{ printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;//pf1和pf2都是函数指针变量
int Add(int x, int y)
{ return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的
函数指针类型解析:
int (*pf3) (int x, int y) | | ------------ | | | | | pf3指向函数的参数类型和个数的交代 | 函数指针变量名 pf3指向函数的返回类型
int ( * ) (int x, int y) //pf3函数指针变量的类型
1.2 函数指针变量的使⽤通过函数指针调⽤指针指向的函数。
#include <stdio.h>
int Add(int x, int y)
{ return x+y;
}
int main()
{ int(*pf3)(int, int) = Add;//相当于pf=add printf("%d\n", (*pf3)(2, 3));//对函数指针变量解引用后传参 printf("%d\n", pf3(3, 5));//因为pf3=add,可以直接调用return 0;
}
输出结果:
5
8
1.3 两段有趣的代码
代码1
(*(void ( * )())0)();
//void(*)()是一个函数指针类型,这个指针指向的函数没有参数,返回类型是void
//(void ( * )())0这段代码是在将0强制转换为void(*)()这种函数指针类型,意味着0地址处有这么一个函数
//(*(void ( * )())0)()对0地址进行解引用,去调用0地址处的这个函数
代码2
浅浅的补充一个知识:函数有三种用法:函数调用,函数声明,函数定义
void (*signal(int , void(*)(int)))(int);
//这段代码没有参数,所以不是函数调用,也没有大括号,所以不是函数定义因此这段代码是是一次函数声明
//声明的这个函数名字是signal
//signal函数有2个参数,第一个参数是int类型
//第二个参数是一个函数指针类型void(*)(int),该函数指针指向的函数的参数是int,返回类型是void
//signal函数的返回类型也是一个函数指针类型void(*)(int),该函数指针指向的函数的参数是int,返回类型是void
//=void (*)(int) signal((int , void(*)(int))但不能这样写
两段代码均出自:《C陷阱和缺陷》这本书
1.3.1 typedef关键字
typedef是⽤来类型重命名的,可以将复杂的类型简单化。
⽐如,你觉得 unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤:
typedef unsigned int uint;
//将unsigned int 重命名为uint
如果是指针类型能否重命名呢?
其实也是可以的,⽐如,将 int* 重命名为 ptr_t ,这样写:
typedef int* ptr_t;
//注意:
int* p1,p2;//p1的类型是int*,而p2的类型是int
ptr_t p3,p4;//p3和p4的类型都是int*
但是对于数组指针和函数指针稍微有点区别: ⽐如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t ,那可以这样写:
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
函数指针类型的重命名也是⼀样的,⽐如,将 void( * )(int) 类型重命名为 pf_t ,就可以这样写:
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
那么要==简化代码2==,可以这样写:
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
举例:
int add(int x,int y)
{return x+y;
}
typedef int(*pf_t)(int,int);
int main()
{//int (*pf)(int,int)=&add;pf_t pf=&add;return 0;
}
再利用代码2演示函数的三种用法
typedef void(*pfun_t)(int);
//函数定义
pfun_t signal(int n,pfun_t p)
{//...return p;//p是函数指针
}
int main()
{pfun_t signal(int,pfun_t);//函数声明,函数声明可以位于main函数之前或之内,只要在调用语句之前即可signal(100,&test);//函数调用return 0;
}
2. 函数指针数组
数组是⼀个存放相同类型数据的存储空间,我们已经学习了指针数组, ⽐如:
int* arr[10]; //数组的每个元素是int*
那要把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];
答案是:parr1
parr1 先和 [] 结合,说明parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。
3.转移表
函数指针数组的⽤途:转移表
举例:计算器的⼀般实现:
#include <stdio.h>
int add(int a, int b)
{ return a + b;
}
int sub(int a, int b)
{ return a - b;
}
int mul(int a, int b)
{ return a * b;
}
int div(int a, int b)
{ return a / b;
}
int main()
{ int x, y; int input = 1; int ret = 0; do {printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf(" 0:exit \n"); printf("*************************\n"); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = add(x, y); printf("ret = %d\n", ret); break; case 2:printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = sub(x, y); printf("ret = %d\n", ret); break; case 3: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = mul(x, y); printf("ret = %d\n", ret); break; case 4: printf("输⼊操作数:"); scanf("%d %d", &x, &y); ret = div(x, y); printf("ret = %d\n", ret); break; case 0: printf("退出程序\n"); break; default: printf("选择错误\n"); break; } }while (input); return 0;
}
使⽤函数指针数组的实现:
#include <stdio.h>
int add(int a, int b)
{ return a + b;
}
int sub(int a, int b)
{ return a - b;
}
int mul(int a, int b)
{ return a*b;
}
int div(int a, int b)
{ return a / b;
}
int main()
{ int x, y; int input = 1; int ret = 0; int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表 do { printf("*************************\n"); printf(" 1:add 2:sub \n"); printf(" 3:mul 4:div \n"); printf(" 0:exit \n"); printf("*************************\n"); printf( "请选择:" ); scanf("%d", &input); if ((input <= 4 && input >= 1)) { printf( "输⼊操作数:" ); scanf( "%d %d", &x, &y); ret = (*p[input])(x, y); printf( "ret = %d\n", ret); } else if(input == 0) { printf("退出计算器\n"); } else { printf( "输⼊有误\n" ); }}while (input); return 0;
}