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

embedded/2024/11/13 10:14:22/

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

目录

一,指针数组

二、指针数组和数组指针的区别

数组指针和指针数组的写法

数组指针的初始化

三、二维数组传参的本质

重点

二、函数指针

什么是函数指针变量呢?

函数指针变量的使用

两端有趣的代码:

1.( *( void ( * )( ) )0 ) ( );

2.void( *signle(int, void( * )(int) ) ) (int)

typedef重命名函数

 四、函数指针数组

函数指针数组的实际应用:转移表

结尾祝福语:


 

一,指针数组

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

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

我们已经熟悉:

  • 整形指针变量: int * pi; 存放的是整形变量的地址,能够指向整形数据的指针。
  • 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
  • 组指针:存放的是数组的地址,能够指向数组的指针变量

二、指针数组和数组指针的区别

数组指针和指针数组的写法

int*parr[10]//指针数组 

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

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

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

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


数组指针的初始化

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

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{int arr[10] = { 0 };int(*p)[10] = &arr;return 0;
}

 我们调试可以看到

 

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

三、二维数组传参的本质

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

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

	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]
     

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

#include <stdio.h>
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) + jarr[i][j] 的地址,进行解引用操作,就是找到 arr[i][j]
     
  • 最终:*( *(p + i) + j) 等价于 arr[i][j]。

二、函数指针

什么是函数指针变量呢?


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

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

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("test:  %p\n", test);printf("&test: %p\n", &test);return 0;
}

 

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

函数指针变量的使用

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

#include<stdio.h>
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等价于Addprintf("%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;
}
  1. int (*pf)(int, int) = Add,*pf外的 () 不能省略。
  2. 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命名一个数组的例子:

#include<stdio.h>
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(*)()类型的函数指针


函数指针数组的实际应用:转移表

 计算器的一般实现路径代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
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;
}

用函数指针实现的:

#include<stdio.h>
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;
}

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

结尾祝福语:

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

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

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

 


 


http://www.ppmy.cn/embedded/114143.html

相关文章

Qt学习之旅 I

构建一个跨平台的应用(Create A Cross-Platform Application) 目录 构建一个跨平台的应用(Create A Cross-Platform Application) 设计模式 开始构建 Qt是跨平台的C框架&#xff0c;这里&#xff0c;我们将会构建一个简单的C跨平台项目来熟悉QT是如何实现简单的跨平台的。 …

上传富文本插入文件时报错:JSON parse error: Unexpected character解决办法

方式一&#xff08;加密解密&#xff09;&#xff1a; 1.前端 &#xff08;1&#xff09;安装 crypto-js npm install crypto-js&#xff08;2&#xff09;util下引入asc.js asc.js import CryptoJS from crypto-js// 需要和后端一致 const KEY CryptoJS.enc.Utf8.parse(…

前端开发规范

前端开发规范 编写背景&#xff1a;当前项目前端开发过程中&#xff0c;每个人有不同的编码风格&#xff0c;这就导致同一个模块不同人开发时可能产生阅读不方便的情况&#xff0c;这对于项目的长久开发是不利的&#xff0c;所以编写这套前端开发规范。 编写目的&#xff1a;避…

google map小叉号不显示

背景需求 需要在uniapp中接入google地图,研究了一番,都没有找到合适的,现在说一下教程。 效果图 前期工作 这两点缺一不可,否则你啥也看不到。 1、电脑安装L-O-U梯 用于访问G-OO-G-LE的API或者创建google map key。 2、手机安装L-O-U梯 用于显示google地图。我就是手…

C++ ——string的模拟实现

目录 前言 浅记 1. 构造函数 2. 拷贝构造函数 2.1 拷贝构造传统写法 2.2 拷贝构造现代写法 3. swap 4. 赋值重载 4.1 赋值重载的传统写法 4.2 赋值重载的现代写法1 4.3 赋值重载的现代写法2 5. 析构函数 6. reserve&#xff08;扩容&#xff09; 7. push_back&am…

Windows10安装cuda11.3.0+cudnn8.5.0,以及创建conda虚拟环境(pytorch)

1、检查电脑驱动版本为561.09&#xff0c;选择cuda版本&#xff0c;下图可知cuda版本<12.6。 nvidia-smi #查看驱动版本&#xff0c;以及最大可以安装的cuda版本 2、Anaconda3-2024.06-1-Windows-x86_64.exe下载&#xff1a; 官网&#xff1a;https://www.baidu.com/link?…

Unity教程(十五)敌人战斗状态的实现

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

如何利用 Visual Studio 和 AI 工具实现高效编程

哪个编程工具让你的工作效率翻倍? 在现代软件开发的世界中,编程效率的提升对开发者来说至关重要。高效编程不仅仅是编写更多的代码,还包括如何减少重复劳动、提高代码质量、加快调试和测试流程等。而 Visual Studio 作为一个功能强大的开发环境(IDE),配合各种 AI 工具,…