目录
- 指针数组
- 数组指针
- 数组指针的应用
- 函数指针
- 函数指针数组
- 函数指针数组的应用(转移表)
- 指向函数指针数组的指针
指针数组
指针数组,即指针的数组, 它不是指针,而是一个存放指针的数组,数组的每一个元素都是指针类型的,例如下面的都是指针数组:
int *arr[10];//指针数组,10元素,每个元素都是整型指针类型的
char *arr[4];//指针数组,4个元素,每个元素都是字符指针类型的
char **arr3[5];//指针数组,5个元素,每个元素都是二级指针类型的
- 1
- 2
- 3
由于[ ]的优先级要比*的优先级高,所以数组名先和[ ]结合,所以它的本质是数组,而它每一个元素都是指针类型的,所以指针数组的每一个元素都占四个字节。
##数组指针
数组指针,即数组的指针,它不是数组,而是指向首元素地址的指针,本质是指针(这个指针存放的是数组首地址的地址,相当于二级指针,这个指针不可移动);例如:
int (*p)[10];
- 1
解释:p先和*结合,说明p是一个指针变量,然后指向的是一个大小为10个整型的数组。所以p是一个指针,指向的是一个数组,叫做数组指针。
####数组指针的使用
我们了解了数组指针的定义,那么数组的地址如何来存储?
int arr[10]={0};
int *p1=&arr;
int (*p2)[10]=&arr;
- 1
- 2
- 3
这三种哪一种比较合适呢?
我们知道:
整型变量的地址应该放在整型指针里面去
字符变量的地址应该放在字符指针里面去 所以数组变量的地址应该放在数组指针里面去
显然第三种p2是比较合适的选择,p2是数组指针,所以存放数组的地址是最合适的。
举个栗子,我们看一段代码:
void print(int(*p)[5], int x, int y)//二维数组的参数(*p)[5]相当于p[][5]
{//p死一个指针,指向一个数组,这个数组有五个元素,每个元素是整型
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%d ", *(*(p+i)+j));//打印二维数组的每个元素
}//p是指针指向首元素地址,也就是第一行的地址,+i就是第几行的地址,解引用就是首元素地址,再+j,就是每个元素的地址,再解引用就是每个元素了
printf("\n");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5,5,4,3,2,1,6,7,8,9,0 };//二维数组
print(arr, 3, 5);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
运行结果如下:
数组指针的应用一般都是二维数组传参的时候用
上面我们了解了指针数组和数组指针,接下来我们辨认一下,下面每个分别是什么?
int arr[5];
int *parr1[10];
int(*parr2)[10];
int(*parr3[10])[5];
- 1
- 2
- 3
- 4
1.很简单,是我们经常见到的一维数组
2.是上面我们了解到的指针数组
3.也是上面我们知道的数组指针
那么第四个到底是什么呢?我们分析一下:
首先parr3是一个数组,因为它和[10]靠的近,数组有10个元素,再从外面看,他是一个数组指针,有5个元素,每个元素是整型,图解如下:
函数指针
通过上面的了解,我们应该进一步知道函数指针,是指向函数的指针,用来存放函数的地址,那么说明函数也是有地址的,那么我们怎么取出函数的地址呢?我们通常取数组的地址是&加数组名,那么取函数地址,是否也可以&加函数名呢?如下:
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", &test);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可见,取出的确实是这个函数的地址,&函数名是整个函数的地址,那么函数名难道是首元素的地址吗?显然函数我们无法谈论首元素,所以在函数取地址的时候&加函数名或是直接函数名取出的都是函数的地址,如下:
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", &test);
printf("%p\n", test);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
了解了函数的地址,那么我们应该如何将函数的地址保存起来,这就要用到函数指针了
我们看如下代码:
void *p();//p先和()结合,所以是一个函数
void (*p)();//p先和*结合,所以是一个指针
- 1
- 2
第一个是函数的声明,而第二个则是函数指针,所以如果我们想把上面的test()这个函数的地址存储起来,就可以用函数指针void (*p)()来存储。下面举出函数指针定义的例子:
int Add(int x, int y)
{
return x+y;
}
int main()
{
int (*p)(int, int) = Add;
printf("%d\n", p(2,3));
Add(3, 4);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
解读两段有趣的代码:
//代码一:
(*(void(*)())0)();
//代码二:
void(*signal(int,void(*)(int)))(int)
- 1
- 2
- 3
- 4
代码一:首先从0入手,0前面的(void(*)())是强制类型转换,是将0强制类型转换成函数指针,再对整体解引用,是找到0地址处的函数,这个函数无参,返回类型是void。
代码二:①signal是一次函数声明
②signal函数的参数,第一个是int,第二个是函数指针,该函数指针指向的函数参数为int,返回类型为void
③signal函数的返回类型为一个函数指针,该函数指针指向的参数是int,返回类型是void
函数指针数组
上面解释了函数指针及其定义,我们知道了函数指针是存放函数地址的,那么如果我们把函数指针的地址存到一个数组中,那这个数组就叫做函数指针数组,那么函数指针数组应该如何定义呢?下面哪种定义方式是正确的?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
- 1
- 2
- 3
答案是parr1,parr1先和[ ]结合,说明parr1是数组,数组的内容则是int (*)()类型的函数指针
函数指针数组的用途:转移表
实现计算器:
方法一:函数调用
一般情况下,我们实现两个数的加减乘除会像下面这种方式写:
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;
while (input)
{
printf("***************************************\n");
printf("**** 1:add 2:sub ****\n");
printf("**** 3.mul 4.div ****\n");
printf("***************************************\n");
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
break;
case 2:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
break;
case 3:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
break;
case 4:
printf("请输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
break;
default:
printf("选择错误\n");
break;
}
printf("ret=%d\n", ret);
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
方法二:使用函数指针数组
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;
}
void menu()
{
printf("******************************\n");
printf("** 1. add 2. sub **\n");
printf("** 3. mul 4. div **\n");
printf("** 0.exit **\n");
printf("******************************\n");
}
void calc(int(*pfun)(int, int))//函数指针
{
int x = 0;
int y = 0;
int ret = 0;
printf("请输入两个操作数:>");
scanf("%d%d", &x, &y);
ret = pfun(x, y);
printf("ret = %d\n", ret);
}
//
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
//转移表
int(*pfun[5])(int, int) = { 0, Add, Sub, Mul, Div };
//定义一个函数指针数组,里面有5个元素,每个元素分别是一个函数
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
calc(Add);
break;
case 2:
calc(Sub);
break;
case 3:
calc(Mul);
break;
case 4:
calc(Div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
运行结果如下:
指向函数指针数组的指针
指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针;那么指向函数指针数组的指针又该如何定义呢?
void test(const char *str)
{
printf("%s\n",str);
}
int main()
{
void(*pfun)(const char *str);//函数指针
void(*pfunArr[5])(const char * str);//函数指针数组
pfunArr[0]=test;
void (*(*ppfunArr)[10])(const char *)=&pfunArr;//指向函数指针数组pfunArr的指针ppfunArr;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet">
</div>