C语言回顾11-04-21

C语言回顾-指针深入


  • 指针与数组

C语言中,指针和数组的关系是十分紧密的,C语言允许对指针进行算术运算,通过这种运算我们可以用指针代替数组下标对数组进行处理。

在前面我们讲过数组名就是他第一个元素的地址,并且数组在内存里是均匀排列的,那么我们是不是可以使用指针来访问一个数组呢?

#include<stdio.h>
int main ()
{
	int a[10]={1,2,3,4,5,6,7,8,9,10},*p;
	p=a;//把数组首地址赋给p,这说明了两个都是地址
	while(p<=a+9)//注意这里的条件,a+9的意思是首地址向后移动10个单位,
		//(+0就是首地址),那么加九就是第十个元素的地址,当p大于第十个元素的地址,while就会跳出
	{
		printf("%d  ",*p);//我们在这打印*p的值
		p++;//然后对p进行自增操作
	}
	getchar();
	return 0;
}

这里while的条件很巧妙,要能理解指针存放的是地址,也是一种数据,是可以比较的。数组又是均匀排列的,所以可以得出这个条件,那么他的运行结果如下: 

我们再来看看指针与数组更深的关系,在这之前要复习一个式子,a[n]等价于*(a+n),我们已经讲过,a是地址,之前的数组下标访问其实就是以从首地址移动一定的单位实现的。很神奇,当你要访问数组第5个元素,以前我们写a[4],现在我们可以写*(a+4)两个完全一样,并且,从第二个也可以看出来,单纯的a+4只是地址,需要用*取出里面的内容。并且,其实数组就是一个静态指针,一旦确定不能被修改,也被被称为指针常亮。

当我们会使用指针,关于数组的问题一般会变的简单许多。比如当我们想传一个数组到一个函数,数据太大浪费内存,我们就可以只传首地址,然后利用指针完成对数组的使用。并且以后还会有二维数组的使用,同样可以利用指针来简化操作。比如我们想要数组的某一行,某一列的数据,该怎么办呢?

行的访问比较简单,因为是连续排列,这里不做介绍。我们重点来看列的控制,二维数组里每一列的元素并不是连续排列的,那要怎么访问呢?首先我们来看第一种方法:

#include<stdio.h>
int main ()
{
	int a[3][3]={1,2,3,
				 4,5,6,
				 7,8,9};
	int *p=a[0];//我们让p指向a的首地址
	while(p<=a[2]+2)//这里是让p从1的地址开始循环到第九个地址
	{
		printf("%d ",*p);//打印*p的值
		p+=3;//p每次移动三个位置
	}
	getchar();
	return 0;
}

这个做法是什么意思呢,他的意思是吧二维数组也看做一维数组,看做一条线,第一行-第二行-第三行这样子,然后只要限定p每次移动的个数和每行的元素个数相同,就好像实现了列的读取,但是这是投机取巧的方法。结果当然可以:

显然这样是可以的,但是我们还有一种稍微难一点的方法,这里就要用到数组指针。顾名思义,就是指向数组的指针.那么具体又什么用呢,看下面这段代码:

#include<stdio.h>
int main()
{
	int a[3][3], (*p)[3], i,j;//定义一个二维数组,一个数组指针,两个整形
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			a[i][j] = 1;//我们把数组元素全部初始化为1
			printf("%d  ", a[i][j]);//在这打印出来
		}
		printf("\n");
	}
	getchar();
	//然后我们尝试把第二列的数字改为2
	for (p = &a[0]; p<&a[3]; p++)//这里是让p从第一行循环到第三行。
		//然后固定队每一行的某一个位置的数据进行操作实现列的改变
	{
		(*p)[1] = 2;
	}
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 3; j++)
		{
			printf("%d  ", a[i][j]);

		}
		printf("\n");
	}
	getchar();
	return 0;
}

效果如下:多理解这种方法,就是用带长度的指针来度量数组,对这个长度的某一个固定位置进行操作,然后在二维就好像是对列进行操作,除此之外还需要理解指针数组,就是和普通的整形数组差不多,只不过是很多指针的集合,和数组指针是不一样的。

  • 指针函数与函数指针

到目前为止,我们学过很多只想给各种数据类型的指针了,整形、字符型、甚至是数组。当时C语言没有要求指针必须只能指向数据,他还允许指针指向函数,被称为函数指针。它没有那么奇怪,函数的实质是一段代码,这一段代码在内存中是连续分布的(一个函数的大括号括起来的所有语句将来编译出来生成的可执行程序是连续的),所以对于函数来说很关键的就是函数中的第一句代码的地址,这个地址就是所谓的函数地址,在C语言中用函数名这个符号来表示。

比如我们要定义一个double类型的函数指针:double (*p)(参数类型),这样就是一个可以指向具有相同类型,相同参数的类型的函数的指针,它的本质是一个指针。

指针函数,很容易理解,就是返回值为指针的函数,和int double相同,本质是一个函数。这个不难理解,不多做介绍,下面我们重点讲一下函数指针。先来看一段代码:

#include<stdio.h>
double MAX(double a, double b)//这是我们打过的计算较大值的函数
{
	if (a > b)return a;
	return b;
}
int main()
{
	double(*p)(double, double) = &MAX;//定义一个函数指针指向MAX
	printf("%lf  ", p(1, 2));//计算1,2的最大值
	getchar();
	return 0;
}

简单使用就是这样,当时这个其实在C语言内是非常有用的,想象一下,如果我们把函数指针在作为参数传递到一个函数内,或者用作数组的元素,再或者用作结构或者联合的成员,甚至可以编写返回函数指针的函数。

  •  指向指针的指针

到这应该是最好理解的了,指针也存储在内存里,它当然有自己的地址,那么就可以用另一个指针来指向它。所以我们把一般使用的指针称为一级指针,指向一级指针的指针称为二级指针。定义也很简单,要指向int *p,的指针只需要定义为:int **P=&p,那么这时候P就是指向p的指针,它的内容就是p 的地址。这里我们可以编程说明:

#include<stdio.h>
int main()
{
	int p, *pp=&p, **ppp=&pp;
	printf("&p=%x\npp=%x\n&pp=%x\nppp=%x",/*这里我们分别打印出p的地址,pp的值,pp的地址,ppp的值*/
		&p, pp, &pp, ppp);
	getchar();
	return 0;
}

打印结果会是什么样呢,我们可以猜想一下,指针存放的是地址,那么pp里存的就是p的地址,ppp里就是存的pp的地址:

可以看见,&p和pp,&pp和ppp的值是相等的,那么我们还学过*寻址符,这里的*ppp,**ppp又会等于什么呢,我们再次给p赋值为10,再打印:

很明显,都打印出了p的值,我们可以思考一下ppp,一个*会提取到pp的地址,然后再*一次,会从pp的地址取出数据,也就是p的值了,也不是很复杂

这里的重点就是理解指针存放的内容和它本身的地址,然后寻址符的运用也很重要。


14 : 52

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值