Fwl的小花园

  博客园 :: 首页 :: 博问 :: 闪存 :: :: 联系 :: :: 管理 ::

C指针基础知识

 

C语言中,指针无疑是最令人头疼的。今天无事就来学学C语言的指针,在此留下点笔记,仅供个人参考。

 

首先要搞懂的是,指针是什么?

指针:是用来存放内存地址的变量。

不管是什么类型的指针,存放的都是内存地址,所有类型的指针变量的大小都是一致的。

和指针相关的两个符号: & 取址符。取一个变量的地址。 * 表示一个指针,同时也可以取指针对应的变量的值。

 

下面来看一下指针最简单的使用。

代码上解释的比较清楚了,来看看打印的结果,前两行打印的结果是一样的,所以可以看出,指针里面存放的就是变量i的地址。

看打印结果的后面两行,可以看出。这两行的结果都是3,倒数第二行打印出来的就比较好理解了,是变量i的值,也就是3;打印结果的最后一样,也是3,*pi 表示取指针变量 pi 存储的地址所对应的变量的值。

说起来有点绕,反正,记住,指针是地址,而 *pi 是取值,取该指针对应的变量的值。

 

这个例子中,有一点需要明白的是:不论改变i的值还是 pi 的值,都不会互相影响。

也就是说,改变了i的值,pi 的值是不会变的,这个好理解,我只是改变了这个变量的值,而我这个变量依然在内存中,还是在那块内存中,所以,只要这i的在内存中的位置(地址)不变,pi 的值就不会变。

同时,改变了 pi 的值,i 的值也是不会变的,例如现在多了一个 int 类型的 j 变量,然后把 j 变量的地址赋给 pi,那么只是改变了 pi 的值,pi 里面存放的是变量j的地址,而变量i的地址和值都不会发生任何改变。

这也就是指针的灵活性,指针可以指向该指针对应的类型的任意变量。(说白了就是 --> pi 能指向任何 int 类型的变量,不能指向其他类型的变量)

 

下面再来看一个简单的例子,交换两个数字。

如果不使用指针的话,很简单,直接搞一个中间变量,就可以交换这两个变量的值了。而使用指针的话,也很简单,照葫芦画瓢嘛。

上面注释掉的是不用指针的时候,一清二楚,不用说都明白了。

下面是使用指针的情况,都说照葫芦画瓢了,既然指针 pi 存放的是变量 i 的地址,那么,*pi 就表示变量 i 的值,那么就好办了,在前面的代码中,用 *pi 替代掉 i 不就好了么,同样的道理,用 *pj 替代掉 j 就好了。

 

最后来看一下下面两个方法,这两个方法是一点更要搞懂的,不然你说我知道什么是指针。那就是扯蛋。

假设我现在不在主函数里面交换两个变量 i j 的值,而是调用一个方法去交换他们的值,现在该调用哪个方法呢?

 

很简单嘛,两个都一样,既然传进去的是int类型的值,那么就直接取指针变量对应的值不就得了,也就是这样:swap( *pi , *pj );

而调用第二个的时候就是 swap( pi , pj );

没错,方法调用是没错的,是这样调用。但能够使值交换的,只有下面这个方法,道理也很简单:

先看上面方法那个代码,方法体中的 x y 都是局部变量,这个方法改变的只是局部变量的值。而主函数里面的确没有改变。所以,这个函数是不能交换其值的.(这也就是值传递)

再来看下面这个方法,下面这个方法是可以,下面方法为什么可以呢?因为传递的是地址。为啥传递的地址就可以?

这个嘛,举个例子。怎么举例呢?居然这个涉及到了内存,那么就举一个看起来吻合一点的例子吧!

有两个房子,地址分别是人民路1号和人民路8号,1号房子里面存放着一个篮球,8号房子里面存放着一个足球。

第一个方法,就好像直接把球复制出来。把复制品去交换,也就是值得传递,而第二个方法则传的是两个房子的地址,跟你说要把两个房子里面的球交换一下。然后你就跑去1号房子,把球抱出来,然后跑到8号房子,交换球,再把球拿回到1号房子。就这么简单。这就是引用传递。

小结:

如何让子函数更改主函数的值?

1:子函数的形参为一个地址的值。

2:子函数去修改形参的地址所对应的变量的值。

3:主函数在使用的时候把要修改的变量的值传递给子函数。

 

指针常见的错误:

1:指针变量在没有赋值之前不能使用。

  没有赋值,也就是垃圾值,在编程上就叫野指针;

2:类型不同的指针不能互相转换。

3:使用指针的时候,不去访问已经被系统回收的数据。

  子函数执行完毕后,子函数的所有局部变量都会被系统回收,如果能够获取到值,那都是影子值,是不能确保该值的正确性;

 

指针还有一个比较灵活的使用,在java中,返回值可以是基本类型,也可以是对象,也可以容器类(数组,集合等),但在C中是不允许返回数组的。那么,我们使用指针可以返回2个以上的参数。

怎么返回呢?很简单啊,既然容器类可以装多个变量,而C中是没有集合的,那就是数组了呗,数组又要返回一个,怎么返回呢?直接返回数组的地址不就好了么!

 

指针的运算:

指针其实也可以像其他变量一样运算。

开什么玩笑,指针也可以计算,指针不是地址么?两个地址运算的结果是什么?例如拿上面那个例子,人民路1号和人民路8号,运算的结果是啥?难道是人民路9号?

没错,你说对了,指针当然不能直接的使用地址进行加减乘除,地址的加减乘除是没有任何意义也不允许这样子做的。

既然不能使用指针去进行加减乘除,那么我们在每一个指针前面加个*,也就是取指针的值进行运算不就得了,没错,这也是对的。

但;这是指针的运算么,这是指针所对应的值的运算,好吧!

既然说是指针的运算,那就是指针的运算,来看下面这个例子:

这个就是一个指针运算的例子,看最后两行的打印语句,变量i不就是指针变量么,指针变量+1和-1不就是指针的运算么?

可以看到的是:打印的第一行,取指针 i 所对应的值,也就是数组arr下标为2的值(数组中第三个,也就是3).

上面这点搞明白了就好办了,指针+1和-1表示取这个指针变量相邻的地址所对应的值。这句话怎么理解呢?也就是说,+1表示取这个位置的下一个,而-1表示取上一个,这个比较好理解。

但它是怎么取得呢?怎么去识别的呢?一个int里面是4个字节。是怎么会自动的向上或向下偏移4个字节的呢?

别忘了,指针也是有数据类型的,虽然指针里面存放的都是地址,但这个地址只能存该指针相对应的变量的地址,所以,指针在运算偏移的时候就会根据指针自身对应的变量的长度来进行偏移。

 

多级指针

说白了,就是指针的指针,其中可以有N层,哈哈。

假设有这样一段代码:

int i=5;
int* pi=&i;
int** ppi=π

第一行很好理解了,第二行我们也知道是什么意思了,接下来看第三行;

第三行就是指针的指针,也就是说,里面存放的是指针的地址,这个怎么理解呢,第二行很好理解吧,指针变量存放的是 int 类型变量i的地址,同样的道理,既然变量i是在内存中存在的,那么变量 pi 也是在内存中实际存在的,所以,变量 pi 也是有地址的,这里存放的就是指针 pi 的地址。其实就好像你去找人,我要去找张三,结果我去到教室。张三不在,同学说张三去了宿舍(一级指针),然后我跑会宿舍,同学说张三在饭堂(二级指针)。如果饭堂没找到,那就是三级指针了。哈哈

这个搞清楚了,接下来就好办了。还是上面这个例子:

pi 里面存放的变量i的地址,那么,*pi 就是取变量i的值,而ppi存放的是 pi 的地址,那么 *ppi 其实取到的就是 pi 的值(变量i的地址),那么要通过 ppi 去取变量i的值,那么就要使用 **ppi 了。这个比较好理解,上面这个理解清楚下面的就好理解了。

 

最后说一点:函数的指针

指针不仅可以是变量的指针,指针的指针,还可以是函数的指针。同理,函数的指针存放的是个函数的地址。

看代码:

 

这就是一个函数的指针的简单的示例代码,代码里面注释也写的比较清楚了。

这里需要注意的是,函数必须写在main函数之前,因为函数必须要先装载进内存,才能知道函数的地址是啥,否则会报错。

 

 

指针基础只是就写到这里:

2016-10-18   21:30

 

posted on 2016-10-18 21:31  Fwl的小花园  阅读(333)  评论(0编辑  收藏  举报