在进入今天的内容前,先放个小杀招……
请看图:
答案我最后再公布,哈哈!
今天讲的是指针,这个玩意儿在C语言中可以说很烦人……很容易搞错……小编我是自学的,有不合理的地方请多多包涵……另外,由于指针内容比较多,也是重点,我可能会分几篇文章,循序渐进的讲述我理解的指针!
先来看指针的声明格式:
数据类型 *指针变量名记住,定义好之后,此指针只能指向定义的该数据类型!否则会出错!新手注意!
这里还有一个易错点,那就是,如果指针没有指向任何变量,即没有赋值或初始化,那么可以对此指针使用间接访问运算符*吗?
答案是不可以!因为指针还没有指向任意变量,换句话说,就是指针内部还没有存储变量地址,既然没有存储变量地址,怎么能取出变量的值呢?是不是?
来个杀招过把瘾!看题:int *p,x=2;*p=5;
请问这样可以吗?
如果你认真看了我上面的内容,那么就不可以。这是一种内存盗用的行为!很危险!因为指针没有初始化,可能指向内存中的任意一个位置,这就导致了后面给他赋值可能会篡改指针原来指向的那个值。如果那个值刚好是应该被其他程序调用的,那么就会造成轻则程序错误,重则程序崩溃的后果!
好,基本的差不多了,下面看几个常见的形式:
int x=1,*p;p=x;
&x等价于x&p等价于&x或者pp+1等价于++xp++等价于*(p++)这里得注意一下优先级
下面我们在来看看指针作为函数的参数会发生什么。(这里有个概念,很多新人,我保证你会错的很惨!)
首先我们就要明确一个概念,实参的值是可以传递给形参的,形参却无法改变实参的值!(如果你不用指针去改变的话),因为函数的参数是单向传递的。
问题来了,如果函数的形参是指针呢?在传递的过程中,哪些地方会变动?整个流程你心中是否有数?
我开始解析了。首先,实参指针会把自己的地址传递给形参,然后形参对地址做出一系列的改变、赋值等操作,这里要注意的是,形参已经拥有了实参所指的地址,也就是说,形参怎么操作都会对实参所指的那个内存地址产生影响!反过来,如果你再对实参进行操作,那么那个内存地址里面的值也会改变。总结成一句话就是:形参实参都会直接对内存修改!(因为是指针)
这里还有一个注意点,那就是函数调用以指针为参数的实数时,形参与实参的指针类型必须是指向同一种数据类型的,即类型相同,否则出错。
还没完,我还有一个坑!请问:可以通过修改指针形参的值来修改指针实参的值吗?我建议读者把这句话读5篇以上再思考!
我不卖关子了,实话告诉你吧!不可以!什么?小编你上面不是说可以修改的吗?怎么这里就不可以了?
我想说,请你看仔细了,很容易理解错这个概念!这里修改的是指针形参的值,即指针形参的地址!通过修改形参的地址,是无法改变实参的地址的!这就和我刚开始提的概念相一致了。单向传递!不过,指针形参所指向的变量值的修改,可以改变对应实参指针指的变量。
最后一个小知识点:假设下面变量都合法,没有语法错误,p1p2是指针,分别指向abexchange(p1,p2)等价于exchange(&a,&b)
这样写也是可以的。
哦对了,差点忘了上面第一题的答案,看图:
简单提示一下吧,fun括号里面的是字符,0是字符不是数值,下面*p!=0这里的0是数值……
我们都知道,指针是可以用来操作数组的,并且,这样子速度还比较快!二数组的名字通常就是数组首元素的地址。比如:
int a[3]={1,2,3}
其中,a就代表首元素1的地址。哦,对了,之前有读者在指针与二维数组的赋值这里有点小疑问,我这里就在说一下。
先来看个二维数组以及数组指针的声明:
int a[2][3];
int *p[3];
p[0]=a[0][1];
我先说明一下,第二行声明的是数组p,这个数组p里面的元素是指针元素,共三个。第三行p[0]的意思是将数组p中的第一个指针元素赋值为a[0][1]这个元素的值。嗯,我这里将p[0]这个元素假设成指针k元素,p[0]就等价于k,后面a[0][1]就是一个值,假设是3,那么就是k=3,我个人不知道这样理解对不对,因为我是想通过这个例子说明上面的三行是正确的。(因为有读者想不通)。
好,我们继续下面的内容。数组与指针之间的转换。
int a[3]={1,2,3}
int *p=a;
p[1]=20;
像第三行这样表示是完全可以的。也可以像下面这样:
int *p=&a[1];
另外,二维数组和指针结合在一起后,通常会让很多人头疼,我们先看看二维数组的一些易错表示:
int a[3][3]假设已经初始化了……我后面不写了。那么后面这几种表示我们得熟悉:
a[1]+1 表示的意思是第二行第二列
*(a[1]+1)等价于a[1][1],记住,这里是数值相等!数值!
(a[1]+1)等价于 ((a+1)+1),后面这种情况要慢慢理解,好好理解。要始终谨记,我们这是在处理二维数组,所以看这个式子的时候,括号里面代表的是二维数组坐标,大括号外的号,才暗示我们输出对应坐标的值。
最后,我想考一下各位,请你们判断我下面的式子是否正确。(以下都是以二维数组为基础)
*(a[1]+0)等价于 *a[1] 吗?
*a[1] 等价于 **(a+1) 吗?
正确答案是,上面两个全都是等价于。小编我还是解释一下吧!
已知这是基于二维数组的,那就表示第一行中的a[1]表示二维数组第二行,a[1]+0表示二维数组第二行第一列,外面又加个括号和号,那就说明这是取那个对应坐标的值。后面的a[1]解释就是将第一列对应的0给消去了,如果没有*号,则表示这是第二行,有了则表明要取值了。
第二个例子a[1]和上面意思一样,就是取二维数组的第二行第一列坐标的值,因为把0列的0消去了,所以干扰了读者的理解。后面的**(a+1)更难理解,我们都知道a代表数组首元素的地址,那么(a+1)则代表二行,这个式子可以扩写为 ((a+1)+0),这样就懂了吧。
指针与函数
先给大家看两个例子,嘿嘿。(小编我又邪笑了!)
int (*p)( a,b );
int *p( a,b );
请问,这两个是一个意思吗?如果你觉得不是,那么他们又各自代表什么意思?
我先不说,我先给大家讲个函数的小知识。那就是“函数代码的存储空间的起始地址又称为函数的入口地址”此外,存放函数入口地址的指针变量就称为指向函数的指针。
是不是还是不清楚什么意思?没事,我们回到上面的两个例子上。
第一行,代表声明一个指向函数的指针。
第二行,代表声明一个函数,他的返回值是指向int类型的指针。
这下很清晰明了了吧!
补充一下,如果要给指向函数的指针赋值,那么只需要:
(*p)=fun;fun是自定义函数
还有一个易错的地方,假如:
int fun(int,int);
那么声明指向这个函数的指针必须要这样声明:
int (*p)(int,int );括号里的形参最好要一致,否则很危险,同理对应的返回值类型也要一致。、
最后,再说一个常见错误!
已有指向函数的指针p
p=fun(a,b);这样赋值给p是错误的,因为这里赋值的是函数的返回值,不是函数入口地址!
并且,记住,函数指针与数组指针的最大不同是——函数指针不能做算数运算,如p+1;虽然C不允许把函数作为函数的的参数,但是我们可以用指针来搞。
好了,大家看完之后有什么感想呢,请在评论区告诉我呦!
文章来源于“危险一瞬间GG”