目录

指针详解(day19)_操作符

5.函数指针

释义:指向函数的指针。

函数指针的创建

实例

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}

int main()
{
int a=1;
int b = 2;
printf("\n%d\n", Add(a, b));
/*printf("%d\n", sizeof(Add));*/
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}

可以通过以上代码得到函数名的一些性质

1)函数名是sizeof()的非法操作对象,应该与sizeof()工作原理有关,操作符无法使得Add函数得到调用,所有不会开辟对应的内存空间。

2)函数名在printf函数中,可以得到相对应的地址,即在函数中嵌套函数时,该函数得到了调用,内存会开辟空间储存该函数,既然已经开辟空间,自然可以得到该函数的地址。

3)函数名与数组名有类似的性质,在传参过程中可以直接传址。但与数组传首元素的地址不同,&Add与Add本质上都是传的整个函数的地址,在传址过程中不做区分。

调用函数会开辟内存,函数名可以传址,那么显然可以创建指针变量接收函数的地址。

int (*Add_p)(int,int) = &Add;
int (*Add_p)(int,int) = Add;

(*Add_p)表明是名为Add_p的指针变量

(int,int)表明是传参为两个int的函数类型

int表明返回值为int型

关于解引用,因为解操作符*要执行顺序要弱于操作符(),所以解引用需加操作符(*函数名)

通过函数指针调用函数(解引用函数指针)

printf("%d\n", (* Add_p)(a, b));

直接调用函数

printf("%d\n", Add(a, b));

实例

void (*signal(int, void (*)(int)))(int)
{}//函数类型声明正确

void (*)(int) signal(int, void (*)(int))
{}//函数类型声明错误

此函数类型的声明过程

1)声明一种名为 signal的函数

2)型参为int,型参为指针(指针类型为函数指针,该函数(函数1)的型参为整型,返回值为void)

3)signal函数的返回类型为指针,(指针类型为函数指针,该函数(函数2)的型参为整型,返回值为void。

实例

void test_1(int x)
{}
void test_2(int x)
{}
void (* signal(int, void (*)(int)))(int)
{}


int main()
{
void (*p_1)(int) = &test_1;
void(*p_2)(int) = signal(1, p_1);
return 0;
}

这段代码编译不过去,报错类型为signal无返回值,说明函数声明格式有错误。能力有限,错误未知。

函数指针的解引用

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int main()
{
int a = 1;
int b = 2;
int (*Add_p)(int, int) = &Add;
printf("%5d%5d%5d\n", Add(a, b), Add_p(a, b), (*Add_p)(a, b));
return 0;
}

对于函数指针

int (*Add_p)(int, int) = &Add;

显然

Add_p(a, b), (*Add_p)(a, b)

两种方式均可实现解引用,即取得函数或者调用函数的功能。由此可知函数指针变量名的两种性质:

1)函数指针的变量名有传址能力,与数组名类似、

2)创建函数指针变量接收函数地址的过程可以简化为更改函数名,调用方式不变。

6.函数指针数组

释义:存放函数指针的数组(函数指针是一种数据类型)

实例

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int Sub(int x, int y)
{
int z;
z = x - y;
return z;
}
int Mul(int x, int y)
{
int z;
z = x * y;
return z;
}
int Div(int x, int y)
{
int z;
z = x / y;
return z;
}
int main()
{
int a = 6;
int b = 2;
int i = 0;
int (*p_arith[])(int, int) = { Add,Sub,Mul,Div };//创建同一类型的函数指针数组
for (i = 0; i < 4; i++) //函数指针类型为 (*)(int,int)
{
printf("%d\n", p_arith[i](a,b));
printf("%p\n", p_arith[i]);
}

return 0;
}

结果

8
00007FF7F91511B8
4
00007FF7F91513D4
12
00007FF7F91513CA
3
00007FF7F91513CF

通过以上结果,可以得出以下结论:

1)对于函数指针,解引用操作符*可以省略。即函数指针变量的指针变量名会退化为函数地址,可以直接对其解引用,也可以直接通过传址调用该函数。

2)函数名不是函数指针,但在封装函数指针过程中,两者可以不做区分。

4)函数名与数组名有类似的特性,函数指针是一种数据类型,对于相同类型的函数指针,可以通过创建数组接收该类指针。

执行以下语句

printf("%d\n", sizeof(p_arith));

结果

32

显然内存分配了32字节,一个指针8字节,存放了4个指针。里面的每一个指针都指向一个函数。

函数指针数组的应用

实例(建议四则运算计算器)

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int Sub(int x, int y)
{
int z;
z = x - y;
return z;
}
int Mul(int x, int y)
{
int z;
z = x * y;
return z;
}
int Div(int x, int y)
{
int z;
z = x / y;
return z;
}

void menu()
{
printf("##########################\n");
printf("#### 1.add 2.sub ####\n");
printf("#### 3.mul 4.div ####\n");
printf("##########################\n");
printf("###### exit:0 #########\n");
}
int main()
{
menu();
int x, y;
int input;
do
{
printf("请输入运算符:");
scanf("%d", &input);//scanf函数会复制换行符\n
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);

switch (input)
{
case 1:

printf("%d\n", Add(x, y));
break;
case 2:
printf("%d\n", Sub(x, y));
break;
case 3:
printf("%d\n", Mul(x, y));
break;
case 4:
printf("%d\n", Div(x, y));
break;
case 0:
printf("退出\n");
break;
default:
printf("输入错误\n");
}
}
while (input);


return 0;
}

通过传址函数简化代码:(函数名传地址)

实例

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int Sub(int x, int y)
{
int z;
z = x - y;
return z;
}
int Mul(int x, int y)
{
int z;
z = x * y;
return z;
}
int Div(int x, int y)
{
int z;
z = x / y;
return z;
}
void clc(int (*p)(int,int))//函数指针类型为 int(*)(int,int)
{
int x=0;
int y = 0;
printf("请输入两个操作数:");
scanf("%d%d", &x, &y);
printf("%d\n", p(x, y));
}

void menu()
{
printf("##########################\n");
printf("#### 1.add 2.sub ####\n");
printf("#### 3.mul 4.div ####\n");
printf("##########################\n");
printf("###### exit:0 #########\n");
}
int main()
{
menu();
int input;
do
{
printf("请输入运算符:");
scanf("%d", &input);//scanf函数会复制换行符\n

switch (input)
{
case 1:
clc(Add);
break;
case 2:
clc(Sub);
break;
case 3:
clc(Mul);
break;
case 4:
clc(Div);
break;
case 0:
printf("退出\n");
break;
default:
printf("输入错误\n");
}
} while (input);


return 0;
}

通过函数指针数组简化代码:

实例

int Add(int x, int y)
{
int z;
z = x + y;
return z;
}
int Sub(int x, int y)
{
int z;
z = x - y;
return z;
}
int Mul(int x, int y)
{
int z;
z = x * y;
return z;
}
int Div(int x, int y)
{
int z;
z = x / y;
return z;
}

int main()
{
int x, y;
int input;
int(*p_arr[])(int, int) = { 0,Add,Sub,Mul,Div };
do
{
menu();
printf("请输入运算符:");
scanf("%d", &input);
if (input > 0 && input < 5)
{
printf("请输入两个操作数:\n");
scanf("%d%d", &x, &y);
int ans = p_arr[input](x, y);//通过函数指针类数组,调用目标函数。
printf("\n%d\n", ans); //取出的为函数指针,直接指向函数无需解引用。
}
else if (input == 0) //笔者这里错误的用了if语句,考虑一下会出现什么错误。
{
printf("退出\n");
}
else
{
printf("输入值错误\n");
}
}
while(input);
return 0;
}

7.指向函数指针数组的指针

此部分略