你必须知道的指针基础-3.指针的移动及指针的危险

一、指针的移动

1.1 指针的向前及向后移动

  指针每次加一就是指针向前移动指针类型对应的字节数。下面通过一个int指针来指向一个int数组,看看指针的加法运算到底是个什么鬼?

    int nums[]={33,55,77,88,99};
    int* ptr = nums;
    printf("%d\n",*ptr);
    ptr++; // ptr是int类型的指针,所以向后移动4个字节
    printf("%d\n",*ptr);
    ptr+=2; // 向后移动2*4 个字节
    printf("%d\n",*ptr);

  运行结果如下图所示:

  可以看到,指针的加法就是向前移动指定类型字节数,在数组中就是指向下一个元素。

  下面再来看看与加法相反的减法,仍然以上面的代码为例,新增一句:ptr--;

    int nums[]={33,55,77,88,99};
    int* ptr = nums;
    printf("%d\n",*ptr);
    ptr++; // ptr是int类型的指针,所以向后移动4个字节
    printf("%d\n",*ptr);
    ptr+=2; // 向后移动2*4 个字节
    printf("%d\n",*ptr);
    ptr--;
    printf("%d\n",*ptr);

  运行结果如下图所示:

  可以看出,减法即代表向后移动指针类型对应的字节数

1.2 char类型指针的移动

  对于char类型的指针移动,实际就是指向下一个字符或上一个字符:

    char s1[]="hello edisonchou.cn";
    char* p=s1;
    p=p+2; // char类型占一个字节,因此这里向后移动2*1个字节
    printf("%s\n",s1);
    printf("%s\n",p);

  这里实现了一个类似于求子串的操作,运行结果如下图所示:

1.3 同类型指针的相减

  同类型指针相减,得出的是相距的数据类型的长度。下面以两个指向同一个int数组的int类型指针为例,验证一下是否得到相距的数据类型的长度:

    int nums[]={33,44,55,66,77};
    int* iP1=nums;
    int* iP2=nums;
    iP2=iP2+3; // 同类型指针相减得出的是相距的数据类型的长度
    printf("The distance is %d\n",iP2-iP1);

  当iP2-iP3得到的是距离是3,这是因为iP2在进行减法操作之前已经向前移动了3个int类型的长度。

二、指针强大但又危险

2.1 从你家到他家

  刚刚了解了指针的移动的强大,现在我们来看看指针的强大所带来的一些“危险”。例如下面一段代码,我们定义了两个int类型的整数。

    int i1=555;
    int i2=666;
    int* p=&i1;
    printf("%d,%d\n",&i1,&i2);
    p--;
    int i3=*p; //从p当前指向的内存中取出4个字节,解释成i3
    printf("%d\n",i3);

  在指针p的定义中,我们指向的是i1。而当我们对p指针进行减法运算移动时,我们发现当前p指针指向的居然不是i1而是i2了。这也就说明,本来声明指针时指向的是你家的地址,而当对指针进行运算操作后却指向了隔壁老王家的地址,这是搞什么鬼!

  可以从运行结果图看出,i1和i2的地址分别为2686740和2686736(是连续的地址),p指针最开始指向的是i1。而当p向后移动之后,此时已经指向了i2。因此,输出的值为666。

2.2 内存访问越界

  继续上面的例子,我们此时再将p指针向后移动,看看此时p指针所指向的内容的值是多少?当我们再把p指针向后移动99999位时,其所指向的内容的值又是多少?

    int i1=555;
    int i2=666;
    int* p=&i1;
    printf("%d,%d\n",&i1,&i2);
    p--;
    int i3=*p; //从p当前指向的内存中取出4个字节,解释成i3
    printf("%d\n",i3);

    p--;
    printf("%d\n",*p);

  当运行程序后,结果变为了下图:

  那么,这个4200782地址是个什么鬼?它存放的又是什么内容?我们不得而知,这也是另一个程序里边某个变量所存储的位置,但现在在我们这个程序中居然通过指针访问到了!这是个可怕的事情!想想,当A程序中的指针通过移动取得了B程序中的内存地址或数据内容,再对指针对其修改数据,这是一件不安全的事儿!想想一帮苦逼程序员辛辛苦苦加班加点做的游戏,轻而易举地就被挂了外挂,本来需要用RMB才能买的积分或者道具让外挂直接给改了,是不是觉得人生已经没有意义啦?

  下面一段代码则展示了,当指针移动的距离过大时,Windows系统会对此访问限制,程序直接报错。

    p-=99999; //这个内存地址可能是无法访问的,又被称为访问越界
    //Windows对此做了访问越界的限制
    printf("%d\n",*p);

  运行结果是,直接崩溃,弹出错误按钮:

  错误框的出现,代表了指针使用不当的危害,会给客户造成一定时间的延迟服务。因此,指针虽然很强大,但是也很危险!

参考资料

  如鹏网,《C语言也能干大事(第三版)》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

JAVA之旅(一)——基本常识,JAVA概念,开发工具,关键字/标识符,变量/常量,进制/进制转换,运算符,三元运算

比如6:6/2 = 3 余 0 3 / 2 = 1 余 1 那就是从个位数开始011,读起来就是110了

20310
来自专栏野路子程序员

【野路子】正则表达式~极速入门图文教程

34980
来自专栏分布式系统和大数据处理

四种简单的排序算法

我觉得如果想成为一名优秀的开发者,不仅要积极学习时下流行的新技术,比如WCF、Asp.Net MVC、AJAX等,熟练应用一些已经比较成熟的技术,比如Asp.N...

17020
来自专栏前端下午茶

JS 原型模式

原型模式(Prototype pattern),用原型实例指向创建对象的类,使用于创建新的对象的类的共享原型的属性与方法。

43510
来自专栏企鹅号快讯

给初学者:JavaScript 的常见注意点

作者: CarterLi 原文:https://segmentfault.com/a/1190000012730162 上篇说了一些 JS 中数组操作的常见误区...

25760
来自专栏大数据文摘

谷歌R语言格式指南

19330
来自专栏WD学习记录

Python数据结构与算法笔记(4)

当数据项存储在诸如列表的集合中时,我们说它们具有线性或顺序关系。每个数据项都存储在相对与其他数据项的位置。在Python列表中,这些相对位置是单个项的索引值。由...

14210
来自专栏mathor

导入:什么是数据结构,为什么要学习数据结构,约瑟夫环的数组实现

25650
来自专栏编程

您真的会用switch吗?

C语言的理念,程序员应该知道自己正在干什么,而且保证自己的所作所为是正确的。 switch知多少 各个case和default的顺序可以是任意的,但习惯上总是d...

22670
来自专栏猿人谷

怎样写解释器

解释器是比较深入的内容。虽然我试图从最基本的原理讲起,尽量让这篇文章不依赖于其它的知识,但是这篇教程并不是针对函数式编程的入门,所以我假设你已经学会了最基本的 ...

23670

扫码关注云+社区

领取腾讯云代金券