深究C语言3.指针篇-下

学习C语言四-指针(下)

目录

一,指针数组和数组指针

一,指针数组

二,数组指针

二,函数指针和指针函数

一,函数指针

二,指针函数

三,最后需要讲一下


咳咳,好久没更新了,最近比较懒,我会慢慢改正的,尽量早点继续和大家分享,也真的希望大家能一起分享和学习。好了,现在开始我们接下来的学习。   

引子:众所周知,指针就是存放地址的一个变量,我们之前的学习中,指针里面存放的就是一些基础变量的地址,而我们现在就来深度研究一下。既然指针是用来存放地址,那么,数组也有地址,函数也有地址,甚至包括我们后面要学习的结构体也有地址,那指针能不能存放他们的地址呢?

答案当然是肯定的,反过来来想,那数组能不能用来存放指针呢?哈哈哈,我们往下面学一学就知道了。

一,指针数组和数组指针

我们通过前面的学习知道,数组是用来存放同种类型元素的自定义类型。而且我们也知道,所有指针都是一种类型的,就是一个普通变量,只不过里面存储的是地址罢了。

所以我们大胆猜测,是一定存在一种数组里面的元素是指针的这样一类数组。

那我们接下来讲一讲。

一,指针数组

故名思意,指针数组就是一个数组里面的元素都是指针,其实也没啥难的,只要你数组学的够扎实,这个内容对你而言就是很简单的。

我也没啥好讲的,直接上例子吧。

#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个字节。

别问为什么就这么点,什么东西就是基础最重要,把前面的那些搞懂,后面的东西是很好理解的。

希望大家能学到一些东西,欢迎大家评论发言。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢吃豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值