咳咳,好久没更新了,最近比较懒,我会慢慢改正的,尽量早点继续和大家分享,也真的希望大家能一起分享和学习。好了,现在开始我们接下来的学习。
引子:众所周知,指针就是存放地址的一个变量,我们之前的学习中,指针里面存放的就是一些基础变量的地址,而我们现在就来深度研究一下。既然指针是用来存放地址,那么,数组也有地址,函数也有地址,甚至包括我们后面要学习的结构体也有地址,那指针能不能存放他们的地址呢?
答案当然是肯定的,反过来来想,那数组能不能用来存放指针呢?哈哈哈,我们往下面学一学就知道了。
一,指针数组和数组指针
我们通过前面的学习知道,数组是用来存放同种类型元素的自定义类型。而且我们也知道,所有指针都是一种类型的,就是一个普通变量,只不过里面存储的是地址罢了。
所以我们大胆猜测,是一定存在一种数组里面的元素是指针的这样一类数组。
那我们接下来讲一讲。
一,指针数组
故名思意,指针数组就是一个数组里面的元素都是指针,其实也没啥难的,只要你数组学的够扎实,这个内容对你而言就是很简单的。
我也没啥好讲的,直接上例子吧。
#define _CRT_SECURE_NO_WARNINGS 1 //随机发牌(指针的综合应用) #include<stdio.h> #include<stdlib.h> #include<time.h> struct card { int face; int suit;//suit是花色,face是点数 }; //发牌 void deal(struct card* wdeck) { int i, m, t; //发牌标记0:未发 1:已发 static int temp[52] = { 0 }; //产生随机数 srand(time(NULL));//括号里面的是种子,函数根据种子的值得到随机数。对rand函数进行初始化,不然rand函数无法产生随机值 for (i = 0; i < 52; i++) { while (1) { //函数并不能做到真正的随机,所以函数的返回值又叫做伪随机数。 m = rand() % 52;//产生从0~51的随机值。 //判断随机产生的牌发过没有,如果未发过,跳出循环,再随机分配花色和点数 if (temp[m] == 0) break; } //再将此牌的值定为1,代表已经发过了 temp[m] = 1; //有四个玩家,一个人13张牌,我也不知道这个式子怎么来的,反正带进去是对的 t = (i % 4) * 13 + (i / 4); //对应下面的指针数组 wdeck[t].suit = m / 13; wdeck[t].face = m % 13; } } int main() { int i; //52张扑克牌 struct card deck[52]; const char* suit[] = { "Heart","Diamond","Club","Space" }; const char* face[] = { "A","K","Q","J","10","9","8","7","6","5","4","3","2" }; //调用函数,参数为结构体指针 deal(deck); for (i = 0; i < 52; i++) { if (i % 13 == 0) { printf("Player %d :\n", i / 13 + 1); } printf("%s of %s\n", face[deck[i].face], suit[deck[i].suit]); } return 0; }
原谅我选出这个题目的时候不地道的笑了,咳咳,这个题目难度确实比较大,但是我相信你们,你们是最棒的,我记得前几天的时候这个随机发牌问题还是热榜头条,哈哈哈。好好看看,确实找不到啥好的例子了。
用指针数组的时候,你只需要搞清楚,它的本质是数组,也就是说,它的便利性和实用性大部分都是数组提供的,那么,数组有啥优越性?
一个是提供了选择的便利性,再一个就是存储的功能。其实好像也没啥。但是就是因为选择的优越性,可以使题目变得更加简便。
二,数组指针
这个东西就也是顾名思义了,就是一个指针指向了一个数组,一个数组那么长,指针怎么去存放那么多地址呢?
这里我们就要知道,C语言中数组是连续存放的,即,只要知道该数组的首地址,我们就可以通过首地址找到该数组中任何一个元素,所以,数组指针同样只是存放了数组首元素的地址,用不同的指针,指向各种元素。常用于下面要讲的嵌套里面,再常用一点的地方就是二维数组里面,为什么不用在一维数组里面呢?你猜。
之前的数组篇里面我专门讲过,二维数组的数组名表示的是第一行的地址,那么此时,我们就可以用一个数组指针指向这个二维数组的数组名,该指针指向的就是二维数组的第一行。
int(parr*)[]=&arr;
这就是数组指针的表达形式。
#include<stdio.h> #define N 3 int main(){ int arr[N][N],j,i,sum=0; int (*p)[3]= &arr; for(i=0;i<N;i++) for(j=0;j<N;j++){ scanf("%d",*(*(p+i)+j)); if(i==j) sum+=a[i][j] } printf("%d",sum); return 0; }
下面的就是一些常见的引用操作,这个字不是我写的,懂?
二,函数指针和指针函数
我们还是和前面一样,我们先来分析,函数指针或者指针函数究竟有什么用呢?我们首先来单独分析一下函数这个概念。
函数能给我们带来什么好处呢?简单来说可以概括成以下几点: 1、降低复杂性:使用函数的最首要原因是为了降低程序的复杂性,可以使用函数来隐含信息,从而使你不必再考虑这些信息。
2、避免重复代码段:如果在两个不同函数中的代码很相似,这往往意味着分解工作有误。这时,应该把两个函数中重复的代码都取出来,把公共代码放入一个新的通用函数中,然后再让这两个函数调用新的通用函数。通过使公共代码只出现一次,可以节约许多空间。
因为只要在一个地方改动代码就可以了。这时代码也更可靠了。
3、限制改动带来的影响:由于在独立区域进行改动,因此,由此带来的影响也只限于一个或最多几个区域中。
4、隐含顺序:如果程序通常先从用户那里读取数据,然后再从一个文件中读取辅助数据,在设计系统时编写一个函数,隐含哪一个首先执行的信息。
5、改进性能:把代码段放入函数也使得用更快的算法或执行更快的语言(如汇编)来改进这段代码的工作变得容易些。
6、进行集中控制:专门化的函数去读取和改变内部数据内容,也是一种集中的控制形式。
7、隐含数据结构:可以把数据结构的实现细节隐含起来。
8、隐含指针操作:指针操作可读性很差,而且很容易引发错误。通过把它们独立在函数中,可以把注意力集中到操作意图而不是集中到的指针操作本身。
9、隐含全局变量:参数传递。转载于:https://www.cnblogs.com/fhrtr/p/3193685.html
这些其实我都不清楚多少,了解一下就好。
好了,还有一点要知道的就是,数组名≠&数组名。但是,函数名=&函数名,&函数名取得就是该函数的地址。
一,函数指针
这个我有例子哦,就是前面就写过的模拟实现计算器的第二种方法。
但是这个是函数指针数组嵌套使用的,这些东西通常都是嵌套使用的,学到现在,大家也都应该 知道,越往后,越都是那些复杂的东西,需要实现的东西多了,需要的语句也就多了,嵌套便是比较简单的一种操作方法了,常见且重要。
提一下,函数指针数组也叫做转移表,这个应该在后面的算法里面会讲到。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void menu(void) { printf("************请选择****************\n"); printf("******1.Add*********2.Sub*********\n"); printf("******3.Mul*********4.Div*********\n"); printf("************0.exit****************\n"); printf("**********************************\n"); } 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; } int main() { //这里并没有用switch结构,这里跟第一个程序不同,上一个程序中input定义在了switch外面,所以没啥问题 //但是在这里,函数的主体就只有一个循环,由于我们用的是do-while循环,最后while后面的input是在循环体外面的, //如果在里面定义input的话,作用域就到不了外面,故,我们把input定义在外面。 int input = 0; do { //这里我们采用函数指针数组的方法。 int (*parr[5])(int, int) = { NULL,Add,Sub,Mul,Div }; int x, y, ret; x = y = ret = 0; //输出菜单 menu(); //选择方法 printf("请选择:>"); scanf("%d", &input); if (input >= 1 && input <= 4) { //初始化数据 printf("请输入2个操作数:"); scanf("%d%d", &x, &y); //用函数指针的方法,用指针调用函数 ret = (parr[input])(x, y); printf("ret =%d\n", ret); } else if (input == 0) printf("程序结束。"); else printf("选择错误,请重新选择\n"); } while (input); return 0; }
建议大家还是去我之前写的文章里去看一看解析,不然我怕你看不懂,嘿嘿。好好理解一下函数指针数组的优越性吧,或者说函数指针的优越性,因为是需要传参的,传进去哪个函数的地址,就实现那个函数的的功能。
返回类型 函数名(参数类型){} int (*p)(int ,int){}//函数指针 int fun(int x,int y){}//普通函数
二,指针函数
指针函数的意思就是一个函数它的返回值是一个指针,也就是一个地址。
写法也与上面及其的相似。
int* fun(int x,int y){}
发现没有,就是少了一个括号,我也挺无语的。这个式子代表的就是,一个叫做fun的函数的返回值是一个int类型的指针。
三,最后需要讲一下
最后讲一下的就是这个二级指针的问题
说实话,现在的学习中很少很少能遇到二级指针,根本很难用到它。
int *pa=&a;//一级指针 int **ppa=pa;//二级指针 ppa=&pa; *ppa=&a; **ppa=a;//二级指针的引用
唯一需要着重讲一下的就是,二级指针加减1,代表的是走了4/8个字节,依配置而定,但是走的长度一定是固定的,因为二级指针中存放的是一维指针的地址,而一维指针占4/8个字节。
别问为什么就这么点,什么东西就是基础最重要,把前面的那些搞懂,后面的东西是很好理解的。
希望大家能学到一些东西,欢迎大家评论发言。