斩---c语言指针于马下!(上)

前言

想必c语言中的指针是大家学习c语言中碰到的大怪兽之一吧,可能会有人和我一样到现在还没有打通这个BOSS的。这个问题也是困扰了我许久的一个问题,随着学习的深入我发现,学好c语言,其他语言都能很快入手,比如说python,Matlab等等。现在没上完网课和复习好功课的我,居然在这里熬夜写博客,也是没谁了。之前,我总是想用放假的时间来弥补上这一缺陷,毕竟没有指针的c语言是没有灵魂的嘛。心动不如行动,经过大半个月的学习,我郑重宣布:我把这个BOSS打通关了!这不,立马上手博客来为大家分享一波我对指针的认识和感悟。其实,这个前言也算是我自己学习过程中的感悟吧。
文章推敲了一个星期,慢慢的干货哦!
我不敢说写得比教材更好,但是我是按照自己理解和难点的解析,相信大家通过阅读我的文章,会对c指针会有更独到的见解。话不多说,看文章!
文章比较长,但是c指针的知识可不能少!大家认真看完文章,相信一定能够掌握指针的!
我们还要目录可以索引,大家也可以根据需要选看,建议收藏保存,有问题也可以向笔者提出!

在这里插入图片描述

指针是什么

具体形象的说,指针就像房间的门牌号,房间里面是我们要数据。我们可以通过地址,来访问地址所指向的数据,就像通过门牌号来找到指定的房间。
话说回来,我们为什么要使用指针呢,普通的引用不香嘛?
但是,指针能够很灵活地帮助我们处理许多问题,节省使用空间,完成简单引用变量所不能完成的算法与程序,因此我们要学习使用指针。

指针变量

我们可以直接引用数据,就像
int a=5;
printf(“a=%d”,a);

或者间接地引用数据,即是用指针来完成
int *a=&5;
printf("%d",*p);
这里a是指向整型变量a的地址,其赋值也可以写作
int *a,b;
b=5;
a=&b;

当然了,指针还有很多类型,比如
float *p;
char *p;
等等

这里需要注意的是,指针都有它的类型,即整形、字符型、浮点型等等,具体来说就是int *p; 中p的就是指向一个整型数据的指针。当然这里不考虑void *p的情况,读者不必纠结于这个,这里只是点一下,让大家了解一下,我们在文章后还会对其进行解释。

现在附上一段代码让大家训练一下指针的定义和使用:

int *pointer1 , *pointer2;
int a , b ; 
pointer1 = &a;
pointer2 = &b;
printf("a=%d , b=%d \n",a,b);
printf("a=%d , b=%d \n",&a,&b);
printf("a=%d , b=%d \n",*pointer1,*pointer2);			//这里大家注意一下

读者可以放入c语言软件中看看结果是否一致。其中*的作用是调用指针变量中的数据,就是找到门牌号就用钥匙开门回家了,*就相当于是钥匙!

这里要说明的一点是,大家想必都会有这种情况吧:
scanf("%d",a);
大家忘记给a前面加上了&,取地址符。这时候exe程序就会无法执行,这是你就一头雾水了,哈哈。
其实,这里的scanf函数就是讲输入的通过地址保存到a中,如果你忘记加&,程序exe当然就无法执行喽。

int a = 5;
printf("%d\n",a);
printf("%d\n",&a);

大家可以根据对上面代码,对过刚才的知识点进行进一步的理解。
在这里插入图片描述

指针作为函数参数

接下就有一点难度了,敲黑板!
指针怎么能不与函数相结合,二者结合才是经典嘛
文字不一定能看懂,但掌握代码就掌握了精髓,看代码:

#include<stdio.h>
int main()
{
	int a,b,*p1,*p2;						//变量定义
	int swap(int *x , int * y);				//函数申明
	a=5,b=10;
	p1=&a;									//将a和b地址给p1和p2
	p2=&b;
	swap(p1,p2);							//调用函数
	printf("a=%d,b=%d\n",a,b);
	return 0;
}

int swap(int *x , int *y)
{
	int temp;								//定义一个存储结果的整型变量
	temp = *x;								//将传入的形参进行运算
	*x = *y;
	*y = temp;
	return 0;
}

上述代码将指针变量p1和p2传入swap函数给x和y,x和p1都是指向a的值,y和p2都指向b的值,对其进行的引用,即是x=*p1=a=5 , *y=*p2=10。
所以程序就是通过指针起到了一个值交换的作用。运行结果如下
在这里插入图片描述
有人就发问了,能不能通过交换地址来改变值呢,将原来的swap函数进行更改后给出如下程序,我们说这是不行的

int swap(int *x , int *y)
{
	int *temp;
	temp=x;
	x=y;
	y=temp;
	return 0;
}

在这里插入图片描述
显然结果并没有起到值交换的作用,这是为什么呢,道理是函数的实参对形参的值传递是单向的,改变形参的值并不能影响到实参。这个点希望大家都能注意一下,这也是我当时学习时候的疑难杂症。

下面是一道程序例题,加深大家对指针作为函数参数的印象
题目:输入三个整数,比较大小并交换值,再从大到小输出。

#include<stdio.h>
int main()
{
	int a,b,c,*p1,*p2,*p3;
	int change(int *x , int *y , int *z);
	p1=&a;
	p2=&b;
	p3=&c;
	printf("input integer number a,b,c:\n");
	scanf("%d %d %d",&a,&b,&c);
	change(p1,p2,p3);
	printf("%d %d %d\n",a,b,c);
	return 0;
}
int change(int *x ,int *y , int *z)
{
	int swap(int *x , int *y);
	if(*x < *y)
	{
		swap(x,y);
	}
	if(*x < *z)
	{
		swap(x,z);
	}
	if(*y < *z)
	{
		swap(y,z);
	}
	return 0;
}
int swap(int *x , int *y)
{	
	int temp;
	temp=*x;
	*x=*y;
	*y=temp;
	return 0;
}

读者可以自己试着写一下,或是运行程序看结果是否符合题设。
在这里插入图片描述

通过指针来引用数组

在之前的c语言学习中,我们知道数组可以这样定义

double a[10]={1,2,3,4,5,6,7,8,9,10};
double b[10];
int i;
for(i=0;i<10;i++)
{
	scanf("%lf",&a[i]);
}

在指针中,我们可以有新的定义和引用方式,使得数据存放更加快捷、方便、整洁。

int *p,a[10];
p=a;		//等价于 p=&a[0]

这就相当于定义了一个指针变量,指向数组a的第一个元素的地址。或者可直接写为

int *p=a;

在循环变量中,我们用i++,i–,--i,++i来使得循环进一步执行,指针中是否也有这一性质呢?
答案是有的!
在前面*p=a的条件下,p++指向的是下一个数组元素的地址,即a[1]。假设p指针的初始地址是2000,运算i++后,p指向的地址变为200+d*sizeof(int)。比如int型的指针变量p的,执行p++后,p的地址变为2000+1*4=2004。对于其他类型的变量地址,增加的地址为指针变量增加量乘以其所占的字符大小,道理都是一样的。

下面是指针数组的引用

int *p,a[10]={1,2,3,4,5,6,7,8,9,10};
p=a;
printf("%d\n",a[5]);
printf("%d\n",*(a+5));
printf("%d\n",a+5);			//输出的是地址

其中的重中之重是*,他能够使得地址转变为其指向的变量,与&的作用相反,就像是一对欢喜冤家。
下面给出一段代码,是指针来引用数组的程序,读者领会一下。

int *p,a[10]={1,2,3,4,5,6,7,8,9,10};
for(p=a;p<a+10;p++)
{
	printf("%d\t",*p);
}
p=a;						//敲黑板!

大家不要小看这最后一行的p=a,他让p重新指向&a[0],否则p的指向超出了a数组的范围,而他指向的是我们所未知的,若指向电脑中重要的数据,会带来不可估计的后果!大家记得在,引用完数组的指针变量后,重新将指针指向最初的数组地址,以便于后序程序的引用。
在这里插入图片描述

数组名作为函数参数

人狠话不多,看程序,该程序的作用是将整型数组中的数据逆序排列:

#include<stdio.h>

int main()
{
	int inv(int x[] , int n);
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	int *p=a;
	printf("the original array is:\n");
	for( ; p<a+10 ; p++)
	{
		printf("%d\t",*p);
	}
	printf("\n");
	p=a;
	inv(a,10);
	printf("the sorted array is:\n");
	for( ; p<a+10 ; p++)
	{
		printf("%d\t",*p);
	}
	printf("\n");
	
	return 0;
}

int inv(int x[] , int n)
{
	int i,j,temp;
	for(i=0 ; i < (n-1)/2 ; i++)
	{
		j=n-1-i;
		temp=x[i];
		x[i]=x[j];
		x[j]=temp;
	}
	return 0;
}

读者可上机调试一下正确性,自己动手,丰衣足食。
也可以将上述的函数声明和函数该做如下

int inv(int *x ,int n);			//函数声明
int inv(int *x , int n)
{
	int *p,temp,*i,*j,m=(n-1)/2;
	p=x;
	i=x,j=x+n--1;
	for( ; p < x+m ; p++,i++,j--)
	{
		temp=*i;
		*i=*j;
		*j=temp;
	}
	
	return 0;
}

上述程序阐明了两种函数中引用数组的形式,本质都是输入函数的地址,对地址进行相应运算,从而达到改变数组中元素,最终达成数组的运算。
这里要什么的是,指针之间的加减法有意义的条件是,两个指针指向的是同一数组的元素,否则无意义。
接下来看一段程序,这是用指针的方法将数组中元素从大到小排序。

#include<stdio.h>
int main()
{
	int *p,a[10];
	int compare(int *x , int n);			//函数申明
	p=a;
	for (; p<a+10 ; p++)
	{
		scanf("%d",p);						//输入参与运算的数组
	}
	printf("the array is :\n");
	for(p=a ; p<a+10 ; p++)
	{
		printf("%d\t",*p);					//先输出输入的数组
	}
	compare(a,10);							//调用函数,虽然只有一句,但是是关键
	printf("the sorted array is :\n");
	for(p=a ; p<a+10 ; p++)
	{
		printf("%d\t",*p);					//输出整理后的数组
	}
	return 0;
}

int compare(int *x , int n)
{	
	int i,j,k,temp;			
	for(i=0 ; i < n-1 ; i++)				//这里除了指针的使用外,交换元素的时候就是用的冒泡法
	{										//基础就是之前排序算法时候的内容
		k=i;								//如果大家有不懂的,可以留言,之后我会出相关的内容博客				
		for(j=i+1 ;j < n ;j++)
		{
			if(*(x+k) < *(x+j))
			{	
				k=j;
				if(k != i)
				{
					temp = *(x+k);
					*(x+k) = *(x+i);
					*(x+i) = temp;
				}
			}
		}
	}
	
	return 0;
}

其实,很多时候,实践才能出真知,看不到程序的时候多打几遍,多调试,疑问的地方再返回来看看书,查查资料,这样进步最大,印象最深刻。程序我都调试过啦,可以用的,大家也可以改一些小地方看看能不能运行成功,自己多思考。
在这里插入图片描述

通过指针引用多维数组

之前讲的是一维数组的使用,那么多维数组又是如何引用的呢,在这里我们以二维数组为例

int a[2][2]={{1,2},{3,4}};		//这是通过定义的时候,直接赋予数组元素的值

我们也可以通过后面的循环来分别对数组中的元素进行定义,这里的知道点是数组那一章中,这里假设大家都已经会了,这里点一下,让大家有个回忆印象。如果不会的话也没事啦,等笔者出了相关文章就反回来复习就好啦。
这里讲指针数组,笔者讲对下表进行一一解释
在这里插入图片描述
先说明一点,&和*是相反的运算,一个是取地址,后者是取指针变量中的值,这一点在之前有讲过。

int a[10],*p;
p=a;

这里p就是数组a的起始地址,也是&a[0],读者也可以这样理解。
星号的作用就是取地址中的值,所以*(a+i)即表示a[i],这是对于一维数组的说明。
对于二维数组,
a[1]代表二维数组第二行的第一个元素的地址,也是第二行的其实地址,其相当于*(a+1)
&a[2][3]表示a[2][3]的地址,也可用*(a+2)+3,其中*(a+2)找到了该元素的所在行,并指向了改行的地址,或是该行的第一个元素的地址&a[2][0],之后再有*(a+2)+3,就找到了a[2][3]的地址,相当于是&a[2][3]。而如果我们要用指针来表示a[2][3]的地址,有了之前的说明,不难写出
*(*(a+2)+3)
读者对上面这个表理解到位,就差不懂能够掌握指针数组的引用了。
为了大家能熟悉一下知识点,笔者准备了如下程序:

#include<stdio.h>
int main()
{
	int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
	printf("%d,%d\n",a,*a);
	printf("%d,%d\n",a[0],*(a+0));
	printf("%d,%d\n",&a[0],&a[0][0]);
	printf("%d,%d\n",a[1],a+1);
	printf("%d,%d\n",&a[1][0],*((a+1)+0));
	printf("%d,%d\n",a[2],*(a+2));
	printf("%d,%d\n",&a[2],a+2);
	printf("%d,%d\n",a[1][0],*(*(a+1)+0));
	printf("%d,%d\n",*a[2],*(*(a+1)+0));
	return 0;
}

调试结果如下,看看他和你想的是不是一样的呢?
在这里插入图片描述

指向n个元素的一维指针数组

读者先看它的定义

int (*p)[4];

注意()是不能省略的哦!否则p会优先于后面的[]结合。
看下面的程序

#include<stio.h>
int main()
{
	int a[4]={1,2,3,4};
	int (*p)[4];
	p=&a;
	printf("%d\n",(*p)[3]);		//输出第三个元素3
	return 0;
}

我们讲一维指针数组也可以用于函数之中,请看下面的程序:
题目:查找有一门课程不及格的学生,输出他们的全部成绩。

#include<stdio.h>
int main()
{
	void search(float (*p)[4] , int n);			//函数声明
	float score[3][4]={{65,57,70,60},{58,87,90,81},{90,99,100,98}};
	search(score,3);				//调用函数
	return 0;
}

void search(float (*p)[4] , int n)
{
	int i,j,flag;
	for(j=0 ; j<n ; j++)
	{
		flag=0;
		for(i=0 ; i<4 ; i++)
		{
			if(*(*(p+j)+i) < 60)
			{
				flag=1;
			}
		if(flag == 1)
		{	
			printf("No.%d fails, and his scores are :\n",j+1);
			for (i=0 ; i < 4 ; i++)
			{	
				printf("%5.2lf",*(*(p+j)+i));
			}
			printf("\n");
		}	
		}
	}
}

指向n个元素的一维数组指针,把多维数组继续降维,使得问题变得更加简单,是我们在编写程序中的很好的选择,希望读者能够多看几遍,把它掌握下来。

预知后事如何,请听下回分解

本来笔者是想一口气把指针的东西一起发出来的,但是因为是第一篇文章,不知道效果如何,写得好不好,而且确实也很多,很难打。综合多种原因后,笔者决定把指针分为上中下部分发表,一来是分段学习,更好完成和接受,二来也想看到读者的反馈啦啦啦!
希望大家多多发表评论交流,笔者看到一定及时回复,我们一起学习进步呀!
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

原创小白变怪兽

帮助原创小白成为怪兽吧!

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

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

打赏作者

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

抵扣说明:

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

余额充值