目录
函数指针
函数名和&函数名
函数指针的定义
函数指针的使用
函数指针陷阱
代码1
代码2
注意
函数指针数组定义
函数指针数组的使用
指向函数指针数组的指针
书写
终于军训圆满结束了,首先回顾一下指针进阶篇(1)主要是指针&数组的点!
C语言之指针进阶篇(1)_唐棣棣的博客-CSDN博客https://blog.csdn.net/m0_74841364/article/details/132223126?spm=1001.2014.3001.5502今天我们继续更深入的来了解指针!主要是指针&函数&数组的点!
函数指针
在前面我们已经了解过字符指针,数组指针,这里我们将介绍到函数指针!
指针数组——是数组,是存放指针的数组。
数组指针——是指针,指向数组的指针。
字符指针——指向字符的指针。
整型指针——指向整型的指针。
浮点型的指针——指向浮点型的指针。
函数指针——指向函数的指针。
函数名和&函数名
类比数组指针。
数组指针——指向数组的指针——存放的是数组的地址——&数组名就是数组的地址
函数指针——指向函数的指针——存放的是函数的地址——函数地址是否是&函数名呢?
那我们用代码来验证下。
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{//int x = 0;//int y = 0;//scanf("%d %d", x, y);printf("%p\n", &Add);printf("%p\n", Add);//&函数名 就是函数的地址//函数名 也是函数的地址//注意函数名并没有首元素这一说法
}
&函数名和函数名,都是函数的地址。
那它们是否有什么区别和它们的类型是一样的吗?
它们没有区别,知识写法不一样。
它们两个类型是一样的。指向同一个函数,类型是相同的。
整形函数指针类型?
函数指针的定义
如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名和&函数名均表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。
那该怎样去写函数指针呢?
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{printf("%p\n", &Add);printf("%p\n", Add);int (*p)(int,int) = &Add;//更容易理解和熟悉int (*p)(int x,int y) = &Add;int (*p)(int x,int y) = Add;//写不写x y都可int (*p)(int,int) = Add;
//&和x y 写不写都可
}
这个语句就定义了一个指向函数的指针变量 p。
首先它是一个指针变量,所以要有一个“*”,即(*p);
其次前面的 int 表示这个指针变量可以指向返回值类型为 int 型的函数;
后面括号中的两个 int 表示这个指针变量可以指向有两个参数且都是 int 型的函数。
所以合起来这个语句的意思就是:
定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。
p 的类型为 int(*)(int,int)。
int (*p) (int, int);
函数返回值类型 (* 指针变量名) (函数参数列表);
定义了一个指针变量 p,该指针变量可以指向返回值类型为 int 型,且有两个整型参数的函数。p 的类型为 int(*)(int,int)。
p就是函数指针变量。
那如果写成int* p(int,int);?
*和int 结合 int* 变成了函数声明了。
函数指针的使用
函数指针的使用最长应用在转移表和回调函数 ,那我们在接下来的文章都会讲到。
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int main()
{printf("%p\n", &Add);printf("%p\n", Add);int (*p)(int,int) = &Add;int ret=(*p)(3, 5);//解引用找到函数int ret = (********p)(3, 5);//*是个摆设,写不写都可int ret = Add(3, 5);//直接调用Add函数就是用Add函数的地址int ret = p(3, 5);//这里也可以直接使用地址printf("%d", ret);
}
我们有以上三种写法去调用函数 Add,但是我们不可以乱写哦!
int ret=* p(3, 5);
函数指针陷阱
阅读两段有趣的代码!
我们改怎样去理解这两端代码呢?
代码1
(*(void (*)())0)();
//函数调用
图错误待修改
- 在调用 0地址处的函数,这个函数没有参数,返回类型是void
- 把0从int类型强制转化成函数指针类型,指向了0处的地址处的函数
代码2
void (*signal(int , void(*)(int)))(int);
//函数声明
图错误待修改
- 这个代码是一次函数声明,声明的是signal函数
- signal函数有两个参数,一个是int类型,一个是函数指针类型,该类型是void (*)(int)
- 函数指针类型 该函数指针指向的函数,参数是int, 返回类型是void
- signal函数的返回类型也是函数指针类型,该类型是void (*) (int)
- 函数指针类型 该函数指针指向的函数,参数是int, 返回类型是void
代码2太复杂了,那能不能简化代码2???当然可以。
void (* signal(int, void(*)(int)))(int);
//void(*)(int) signal(int void(*)(int));❌
//重定义
typedef void(*pfun_t)(int);
pfun_t signal(int pfun_t);
注 :推荐《C陷阱和缺陷》
注意
- 函数指针变量&函数指针&函数指针类型
- 函数指针类型 修饰一个 函数指针变量(可以是函数等 / 也可以没有/或者 将其他类型强制转化成函数指针类型) 意味着则该函数指针指向一个 返回类型是,参数是的函数
- 函数指针定义
int(*p)(int,int)=Add;
- 函数指针调用
(*p)(3,5); p(3,5); (*p)(); p();//不传参数
- 函数指针声明
void (* signal(int,int))(int);
- 函数指针类型和函数指针变量的写法
int(*p)(int,int); void(*p)(int);
函数指针数组定义
char * arr[5]字符指针数组——数组——存放的是字符指针
int * arr[5]整形指针数组——数组——存放的是整形指针
int(*p[5])(int,int) void(*p[5])(int,char)等等
函数指针数组——数组——存放的是函数指针(函数的地址)
把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
int (*parr1[10]])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1 parr1 先和 [] 结合,说明parr1是数组。
数组的内容是什么呢? 是 int (*)() 类型的函数指针。
#include<stdio.h>
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int main()
{int(*p1)(int, int) = &Add;int(*p1)(int, int) = ⋐//函数指针数组中存放的类型相同的多个元素int(*p[])(int, int) = { &Add,&Sub };//p是函数指针数组_存放函数指针的数组//均是返回类型为int,函数参数是int,int类型
}
函数指针数组的使用
函数指针数组的用途:转移表。
使用条件:函数类型/函数参数类型必须一摸一样
#define _CRT_SECURE_NO_WARNINGS 1
//计算器
#include<stdio.h>
void meau()
{printf("**************************\n");printf("** 1.add 2.sub ****\n");printf("** 3.mul 4.div ****\n");printf("** 0.exit *****\n");printf("**************************\n");
}
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;do{meau();printf("请选择>");scanf("%d", &input);switch (input){case 1:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Add(x, y);printf("ret=%d\n", ret);break;case 2:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Sub(x, y);printf("ret=%d\n", ret);break;case 3:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Mul(x, y);printf("ret=%d\n", ret);break;case 4:printf("请输入2个操作数:");scanf("%d %d", &x, &y);ret = Div(x, y);printf("ret=%d\n", ret);break;case 0:printf("退出游戏");break;default:printf("选择错误,重新选择\n");break;}} while (input);return 0;
}
能不能让代码变得简单一点?
//简化后
#include<stdio.h>
void meau()
{printf("**************************\n");printf("** 1.add 2.sub ****\n");printf("** 3.mul 4.div ****\n");printf("** 0.exit *****\n");printf("**************************\n");
}
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int Mul(int x, int y)
{return x * y;
}
int Div(int x, int y)
{return x / y;
}
int main()
{int input = 0;int x = 0;int y = 0;int ret = 0;//用函数指针数组//do{meau();printf("请选择>\n");scanf("%d", &input);int(*p[])(int, int) = { NULL,&Add,&Sub,&Mul,&Div };//0 1 2 3 4if (input == 0)printf("退出游戏\n");else if (input > 0 && input <= 4){printf("请输入两个操作数:\n");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret=%d\n", ret);// ret = p[input](x, y);//用函数指针数组的下标找到指向的函数}else//其他printf("选择错误,请重新选择!\n");} while (input);return 0;
}
当然出了使用函数指针数组去更高效的使用计算器,下章我们将使用回调函数去高效优化计算器!
指向函数指针数组的指针
指向函数指针数组的指针是一个 指针 指针指向一个 数组 ,数组的元素都是 函数指针 。
指向整型指针数组的指针
#include<stdio.h>
int main()
{int a = 0;int b = 0;int c = 0;int* arr[] = { &a,&b,&c };//整形指针数组int* (*p)[3] = &arr;//p是指针,是指向整形指针数组的指针return 0;
}
函数指针数组的指针
#include<stdio.h>
int main()
{int(*arr[5])(int, int) = { NULL,&Add,&Sub,&Mul,&Div };p = &arr;//存放函数指针数组的指针int(*(*p)[5])(int, int) = &arr;return 0;
}
书写
无论是函数指针&函数指针数组&函数指针数组的指针等等,可以一直延申下去!
我们在书写变量是,首先将变量p写出来,在添加其类型。
也可以从简单的函数指针的基础上修改!
✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!最近的心情多做事少说话。
代码------→【gitee:https://gitee.com/TSQXG】
联系------→【邮箱:2784139418@qq.com】