【C语言】(指针系列3)数组指针+函数指针+typedef+函数数组指针+转移表

【C语言】(指针系列3)数组指针+函数指针+typedef+函数数组指针+转移表

前言:前言:开始之前先感谢一位大佬,清风~徐~来-CSDN博客,由于是时间久远,博主指针的系列忘的差不多了,所以有顺序部分借鉴了该播主的,同时也加入了博主自己的理解,有些地方如果解释的不到位,请翻看这位大佬的,感谢大家!!!!!!

一,指针数组思考:我们上一系列已经知道什么是数组指针了,那么指针数组又是什么哪?

答案是:指针数组是一个数组是数组,储存的是地址(首元素),数组指针是数组还是指针哪?答案是--------指针变量

我们已经熟悉:

整形指针变量: int * pi; 存放的是整形变量的地址,能够指向整形数据的指针。浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。组指针:存放的是数组的地址,能够指向数组的指针变量 二、指针数组和数组指针的区别数组指针和指针数组的写法 int*parr[10]//指针数组

解释:数组名先和[]结合,说明这是一个数组,数组中有10个元素,元素的类型是(int*)型,所以p2是一个数组,数组元素指针,叫做指针数组。

int(*p2)[10]//数组指针

解释:p2先和(*)结合说明p2是一个指针变量,然后指针指向的是一个大小为10个整形的数组,,所以p2是一个指针,叫做数组指针。

[]的优先级要高于*号,所以必须要叫()来提高优先级,确保p2与*结合

数组指针的初始化数组指针是用来存放数组地址的,那怎么才能获得数组地址?这里就要用到&的符号,如果要存放数组地址,就得存放数组指针变量中

代码语言:javascript代码运行次数:0运行复制#define _CRT_SECURE_NO_WARNINGS 1

#include

int main()

{

int arr[10] = { 0 };

int(*p)[10] = &arr;

return 0;

} 我们调试可以看到

我们调试也能看到 &arr 和 p 的类型是完全⼀致的。

三、二维数组传参的本质我们将之前,先来回忆一下一维数组传参,一维数组传参为了避免开辟额外的空间,所以只需要传数组的首地址即可

我们再来看我们曾经写过的一段二维数组传参的一段代码:

代码语言:javascript代码运行次数:0运行复制 void test(int a[3][5], int r, int c)

{

int i = 0;

int j = 0;

for (i = 0; i < r; i++)

{

for (j = 0; j < c; j++)

{

printf("%d ", a[i][j]);

}

printf("\n");

}

}

int main()

{

int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };

test(arr, 3, 5);

return 0;

}这里实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?

重点

二维数组在内存中是连续存储的

二维数组可以理解为一维数组的数组,二维数组的每一行都可以看成一个一维数组

二维数组名也是元素的首地址,这里的首地址是指第一行首元素,传过去的是第一行这个一维数组的地址,也就是arr[0]的地址。

第一行的一维数组的类型就是int[5],所以第一行的地址的类型就是int(*) [5]

二维数组传参,形参的部分可以写成数组形式,也可以写成指针形式,如下:

代码语言:javascript代码运行次数:0运行复制#include

void test(int(*p)[5], int r, int c)

{

int i = 0;

int j = 0;

for (i = 0; i < r; i++)

{

for (j = 0; j < c; j++)

{

printf("%d ", *(*(p + i) + j));//等价于p[i][j]

}

printf("\n");

}

}

int main()

{

int arr[3][5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} };

test(arr, 3, 5);

return 0;

}

p:数组首元素的地址,也就是一维数组arr[0]的地址。

p+i:跳过 i 个 int[5] 这样的数组(p的类型是数组指针),指向arr[i],p+i 就是一维数组 arr[i] 的地址。

*(p+i):访问一维数组arr[i],等价于一维数组arr[i],而 arr[i] 是数组名,又是数组首元素的地址,也就是 arr[i][0] 的地址。

*(p + i) + j:由于 *(p+i)是 arr[i][0] 的地址,所以 +j 跳过 j 个整形(指向整形),也就是 arr[i][j] 的地址。

*( *(p + i) + j):由于 *(p + i) + j 是 arr[i][j] 的地址,进行解引用操作,就是找到 arr[i][j]。

最终:*( *(p + i) + j) 等价于 arr[i][j]。二、函数指针什么是函数指针变量呢?

根据前面学习整型指针,数组指针的时候,类比一下,我们不难得出结论:

函数指针变量应该是用来存放函数地址的,未来通过地址能够调用函数的。那么函数是否有地址呢?我们做个测试:

代码语言:javascript代码运行次数:0运行复制#include

void test()

{

printf("hehe\n");

}

int main()

{

printf("test: %p\n", test);

printf("&test: %p\n", &test);

return 0;

}函数的地址确实是有的,同时也验证了函数的地址也是可以用&调取出来的。同时我们观察发现,函数名和取出来的地址是一样的

函数指针变量的使用如果我们要将函数的地址存放起来,就得创建函数指针变量咯,函数指针变量的写法其实和数组指针非常类似。如下:

代码语言:javascript代码运行次数:0运行复制#include

int Add(int x, int y)

{

return x + y;

}

int main()

{

//int a = 10;

//int* pa = &a;//整型指针变量

//int arr[5] = {0};

//int (*parr)[5] = &arr;//parr 是数组指针变量

//arr:数组首元素的地址 &arr:数组的地址

//&函数名和函数名都是函数的地址,没有区别

//printf("%p\n", &Add);

//printf("%p\n", Add);

//int(*pf3)(int, int) = Add;

//int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

//int (*pf)(int,int) = &Add;//pf 函数指针变量,()不能省略

int (*pf)(int, int) = Add;//pf 函数指针变量

int ret1 = (*pf)(4, 5);

int ret2 = pf(4, 5);//pf等价于Add

printf("%d\n", ret1);

printf("%d\n", ret2);

int ret = Add(4, 5);

printf("%d\n", ret);

//int (*pf)(int x, int y) = &Add;//pf 函数指针变量

//int (*)(int,int) 函数指针类型

return 0;

}int (*pf)(int, int) = Add,*pf外的 () 不能省略。pf == (*pf) == Add == &Add。两端有趣的代码:1.( *( void ( * )( ) )0 ) ( );在这里插入图片描述在这里插入图片描述2.void( *signle(int, void( * )(int) ) ) (int)在这里插入图片描述typedef重命名函数 typedef是用来重命名的,可以将复杂的名字简单化,规范化

比如我们命名了一个结构体叫做jinfsjajngijiasogjoiasjda(随便打的),我们每次调用都要写很长一段复杂的东西,但是有了typedef这个东西,我们可以将它重命名为js,对!就这两个字符,就可以表达这个结构体

typedef unsigned int uint;

//将unsigned int 重命名为uint

如果是指针类型,能否重命名呢?其实也是可以的,比如,将 int* 重命名为 ptr_t ,这样写:

typedef int* ptr_t;

但是对于数组指针和函数指针稍微有点区别:

比如我们有数组指针类型 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);

重点

给出typedef命名一个数组的例子:

代码语言:javascript代码运行次数:0运行复制#include

int main()

{

typedef int IntArray[5]//定义一个包含5个int类型元素的数组类型IntArray

IntArray arr={1,2,3,4,5};// 使用 IntArray 声明一个数组

for(int i=0;i<5;i++)

{

printf("%d",arr[i]);

}

return 0;

} 四、函数指针数组定义:

int(*parr[3])();

解释:parr先和[]结合表明这是一个数组,数组的内容是什么,是int(*)()类型的函数指针

函数指针数组的实际应用:转移表 计算器的一般实现路径代码:

代码语言:javascript代码运行次数:0运行复制#define _CRT_SECURE_NO_WARNINGS 1

#include

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");

}

int main()

{

int x = 0;

int y = 0;

int input = 0;

int ret = 0;

do

{

menu();

printf("请输入:");

scanf("%d", &input);

switch (input)

{

case 0:

break;

case 1:

printf("请输入两个数:");

scanf("%d %d", &x, &y);

ret = Add(x, y);

printf("%d+%d=%d\n", x, y, ret);

break;

case 2:

printf("请输入两个数:");

scanf("%d %d", &x, &y);

ret = Sub(x, y);

printf("%d-%d=%d\n", x, y, ret);

break;

case 3:

printf("请输入两个数:");

scanf("%d %d", &x, &y);

ret = Mul(x, y);

printf("%d*%d=%d\n", x, y, ret);

break;

case 4:

printf("请输入两个数:");

scanf("%d %d", &x, &y);

ret = Div(x, y);

printf("%d/%d=%d\n", x, y, ret);

break;

default:

printf("输入错误,请重新输入\n");

break;

}

} while (input);

return 0;

}用函数指针实现的:

代码语言:javascript代码运行次数:0运行复制#include

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");

}

int main()

{

int x = 0;

int y = 0;

int input = 0;

int ret = 0;

do

{

menu();

printf("请输入:");

scanf("%d", &input);

int(*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };//转移表

if (input == 0)

{

break;

}

else if (input >= 1 && input <= 4)

{

printf("请输入两个数:");

scanf("%d %d", &x, &y);

ret = arr[input](x, y);

printf("ret=%d\n", ret);

}

else

{

printf("输入错误,请重新输入\n");

}

} while (input);

return 0;

} 不仅仅可以实现加减乘除,还能实现按位与,或,异或,左移,右移等操作,只需在数组中追加函数地址即可,当然前提是将函数敲出来,这种就叫作转移表。

结尾祝福语:指针系列三到这里就结束了,我们对指针的熟悉又近了一步,指针说是新手的噩梦,其实当你完全了解之后,指针也只不过是取经之路的一难而已,当我们取完经之后,我们会发现,关关难过关关过,斗罢艰险再出发!!!!!!!

最后:风会带来故事的种子,时间是指发芽,但也请不要忘记,旅途中你所看到的风景,人最不能忘的就是初心!!!!!

我们指针系列四再会!!!!!!!!!!!!!!!!!!!!!!!!