网站建设 php 企业网站,企业网属于什么网,网站开发接入本地天地图,网站改版什么意思目录
✍0.前言
#x1f680;1.字符指针变量
#x1f685;2.数组指针变量
#x1f431;#x1f3cd;2.1.数组指针变量是什么
#x1f431;#x1f3cd;2.2数组指针变量怎么初始化
#x1f6a2;3.二维数组传参的本质
#x1f680;4.函数指针变量
✈4.1函数指…
目录
✍0.前言
1.字符指针变量
2.数组指针变量
2.1.数组指针变量是什么
2.2数组指针变量怎么初始化
3.二维数组传参的本质
4.函数指针变量
✈4.1函数指针变量创建
✈4.2函数指针变量的使用
5.函数指针数组
6.转移表
结束语 ✍0.前言
在上一节指针中我们讨论了一系列和指针相关的知识包括二级指针指针数组但光靠这些知识还无法完全理解指针因为指针还有函数指针这样一极其关键的类型而我们上一章也没有具体聊如何存储一个字符串以及如何访问它。好了下面就随着小赵的步伐一起来看看这些知识吧。
1.字符指针变量
首先我们回顾一下我们的字符指针的定义。
int main()
{char m a;//定义一个字符变量char* ch m;//字符指针变量接收m的地址*ch w;//对ch解指针对m里面的值进行修改return 0;
}
我们在上一章是讲过这种单个字符的指针的变量但如果我们存入字符指针的是一个字符串而不是字符结果又会是怎么样的呢
int main()
{const char* pstr abcdfe;printf(%s, pstr);return;
} 那这里的运行原理又是怎么样的呢是不是我们就是把一个字符串放入了指针里呢?其实不是因为指针也无法存储这种数据它存储的毕竟是地址那究竟是咋回事呢其实这里我们的指针存放的是我们这个字符串的首地址 可以看这样的一个代码 这样就证明了我们的我字符指针存储的其实是我们的字符串的首字母的地址。那么知道了这些知识我们就可以去看一道极其有意思的题目
int main()
{char str1[] hello world;char str2[] hello world;const char* str3 hello world;const char* str4 hello world;if (str1 str2){printf(str1 and str2 are same\n);}else{printf(str1 and str2 are not same\n);}if (str3 str4){printf(str3 and str4 are same\n);}else{printf(str3 and str4 are not same\n);}return 0;
}
大家可以先自己猜测一下这个代码运行的结果然后和小赵这边的答案比对一下。 从这个结果我们可以惊讶的发现我们的str3和str4居然是一样的这又是为什么呢?
首先我们来说一下为什么str1和str2不一样因为两个数组开辟的地址不一样两个存了都在数组里存了一样的代码而已。
接着就是我们的str3和str4首先是他们接收的地址来自字符串而这个字符串其实是在我们的内存中开辟一块地区的这块地区是这个字符串的那么我们的地址去访问这个字符串的时候其实访问的地方是一样的就是都用了一个字符串的首字母的地址。
上面这个题目来自我们的《剑指offer》这样一本书各位如果有兴趣可以去看看哦。
2.数组指针变量
在上一章小赵和大家聊了我们的指针数组即在数组中存储指针那么我们是否存在指针去存储我我们的数组呢答案是有的那么我们来看看它长什么样子吧。 在这里我们可以对比一下我们的指针数组 在这里我们发现当我们的*与ptr结合时候他就是个指针那么其*ptr[10]也就是个数组指针而另一个呢我们发现*是与int结合那么它表示的就是我们数组里面存的是指针类类型也就是指针数组啦。
3.二维数组传参的本质
在上一章小赵与大家重点聊了一维数组传参的本质说一维数组传参其实传的是首元素的地址那么我们二维数组根据这个推广其实大家也可以猜到其实传的就应该是二维数组首元素的地址那么二维数组的首元素是什么呢首先我要带大家回想的就是二维数组是由什么组成的在前面小赵重点和各位聊过二维数组其实就是由一个个的一维数组组合而成那其里面的元素就是我们的一维数组那我们传入的首元素的地址其实不就是我们的二维数组里面首个一维数组的地址吗那知道了这个我们就可以试试去用我们上述的数组指针去代替我们原本的代码。
原本的代码
#include stdio.hvoid 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;
}
用指针后的
#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));}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;
} 其实区别并不大小赵在这里只是解释了一里面的本质方便大家更进一步的的理解之前我们的代码在使用方面各位可以自由选择。 4.函数指针变量
在开始我们的函数指针变量之前啊我们要先看看我们的函数是否存在地址然后就是它是否和数组一样是名字就是他们的地址呢?只有知道了这两点我们才能更好地开展我们下面的函数指针。
void test()
{printf(hehe\n);
}
int main()
{printf(test:%p\n, test);printf(test:%p\n, test);
} 在这里我们看到我们两个地址是一样的那其实也就说明了1函数有地址2函数名就是他们的地址。
4.1函数指针变量创建
其实函数指针的创建我们可以参考一下数组指针我们发现在数组指针的创建过程中大体并没有大的变化只是变化了数组的名字改为了指针的名字那么我们的函数指针创建也是如此。
void test()
{printf(hehe\n);
}
void (*pf1)() test;
void (*pf2)() test;
int Add(int x, int y)
{return x y;
}int(*pf3)(int, int) Add;
int(*pf3)(int x, int y) Add;//x和y写上或者省略都是可以的 4.2函数指针变量的使用
#include stdio.h
int Add(int x, int y)
{return x y;
}
int main()
{int(*pf3)(int, int) Add;printf(%d\n, (*pf3)(2, 3));printf(%d\n, pf3(3, 5));return 0;
} 4.3两段有趣的代码 好了学习了上面的一系列知识后我们下面给各位上盘大菜就是我们的这样两段代码也是小赵发在我们的博客上求助的两段代码。
(*(void (*)())0)();
先看第一个如何理解这样一段代码呢 小赵在这里为大家画个图这个图是由里面向外看的。对这个代码做个较为清晰的解释。
第二段代码是
void (*signal(int , void(*)(int)))(int);
在这里小赵也是给大家画一个图方便大家理解 这两段代码出自我们的《C陷阱与缺陷》这本书有兴趣的小伙伴可以看看这本书可以提升大家对于C语言的理解和认知让大家更好地学习和使用我们的C语言。 补充typedef关键字
看了上面两段代码我们有时候可能会感觉我们这个返回类型好大一块还要分开写这样好像并不方便我们的C语言读者的阅读和使用那有木有办法让我们的代码缩短呢,让返回类型只在函数名前面也好啊答案是有的这里就要引入我们的typedef这个关键字这个关键字是干嘛的呢我们可以先上网搜搜看小赵这里用的网站是C 库函数 – srand() | 菜鸟教程 我们可以看到它的主要作用其实就是改名把我们原本的类型改掉 名字有了这个我们就可以优化前面的代码了。 当然还可以用来修改其他的如int 注意一下修改名字的位置就可以了。
5.函数指针数组
上一章我们讨论了指针数组那这一章我们学了函数指针是否可以定义一个函数指针数组呢来看下面。
int (*parr[3])();//parr 先和 [] 结合说明 parr1是数组而数组的元素就是外面这一圈int (*)() 类型的函数指针
接着我们可以试着创建自己的函数指针数组
int add(int a, int b)
{return a b;
}
int sub(int a, int b)
{return a - b;
}
int main()
{int(*p[2])(int x, int y) { add, sub };
}
这里要注意的是我们函数指针数组定义好的元素类型是不能改的不能有的是传入一个元素有的是传入两个也不能一个有返回值一个没有或者返回类型不行。 6.转移表
好了小赵下面带大家做一个转移表同时使用一下我们的函数指针数组
#include stdio.h
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;do{printf(*************************\n);printf( 1:add 2:sub \n);printf( 3:mul 4:div \n);printf( 0:exit \n);printf(*************************\n);printf(请选择);scanf(%d, input);switch (input){case 1:printf(输⼊操作数);scanf(%d %d, x, y);ret add(x, y);printf(ret %d\n, ret);break;case 2:printf(输⼊操作数);scanf(%d %d, x, y);ret sub(x, y);printf(ret %d\n, ret);break;case 3:printf(输⼊操作数);scanf(%d %d, x, y);ret mul(x, y);printf(ret %d\n, ret);break;case 4:printf(输⼊操作数);scanf(%d %d, x, y);ret div(x, y);printf(ret %d\n, ret);break;case 0:printf(退出程序\n);break;default:printf(选择错误\n);break;}} while (input);return 0;
}
在这个转移表中我们其实就是奖我们的各个功能能够灵活调用有点像我们的计算机那我们能否用我们的函数指针数组对其进行改造呢答案是可以的。
#include stdio.h
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;int(*p[5])(int x, int y) { 0, add, sub, mul, div }; //转移表do{printf(*************************\n);printf( 1:add 2:sub \n);printf( 3:mul 4:div \n);printf( 0:exit \n);printf(*************************\n);printf(请选择);scanf(%d, input);if ((input 4 input 1)){printf(输⼊操作数);scanf(%d %d, x, y);ret (*p[input])(x, y);printf(ret %d\n, ret);}else if (input 0){printf(退出计算器\n);}else{printf(输⼊有误\n);}} while (input);return 0;}
我们会发现我们是用我们的函数指针数组让我们的代码更加美观优化更加好阅读。
结束语
好了小赵今天的分享就到这里了如果大家有什么不明白的地方可以在小赵的下方留言哦同时如果小赵有什么地方说得不对也希望得到大家的指点谢谢各位家人们的支持。你们的支持是小赵创作的动力加油。 如果觉得文章对你有帮助的话还请点赞关注收藏支持小赵如有不足还请指点小赵及时改正感谢大家支持