开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 1217|回复: 1
收起左侧

[其它] C++基础之指针的详细介绍(二)

[复制链接]

发表于 2013-2-18 20:33:35 | 显示全部楼层 |阅读模式   山东省济南市
本篇说明C++中的重中又重的关键——指针类型,并说明两个很有意义的概念——静态和动态。希望对你有帮助,一起来看。


本篇是上一篇的续篇,接着为大家介绍C++中的指针
在堆上分配内存
前面已经说过,所谓的在堆上分配就是运行时期向操作系统申请内存,而要向操作系统申请内存,不同的操作系统提供了不同的接口,具有不同的申请内存的方式,而这主要通过需调用的函数原型不同来表现。由于C++是一门语言,不应该是操作系统相关的,所以C++提供了一个统一的申请内存的接口,即new操作符。如下:

    unsigned long *pA = new unsigned long;   
  • *pA = 10;  
  • unsigned long *pB = new unsigned long[ *pA ];
上面就申请了两块内存,pA所指的内存(即pA的值所对应的内存)是4字节大小,而pB所指的内存是4*10=40字节大小。应该注意,由于new是一个操作符,其结构为new <类型名>[<整型数字>].它返回指针类型的数字,其中的<类型名>指明了什么样的指针类型,而后面方括号的作用和定义数组时一样,用于指明元素的个数,但其返回的并不是数组类型,而是指针类型。
应该注意上面的new操作符是向操作系统申请内存,并不是分配内存,即其是有可能失败的。当内存不足或其他原因时,new有可能返回数值为0的指针类型的数字以表示内存分配失败。即可如下检测内存是否分配成功。

    unsigned long *pA = new unsigned long[10000];  
  • if( !pA )? // 内存失败!做相应的工作

上面的if是判断语句,下篇将介绍。如果pA为0,则!pA的逻辑取反就是非零,故为逻辑真,进而执行相应的工作。
只要分配了内存就需要释放内存,这虽然不是必须的,但是作为程序员,它是一个良好习惯(资源是有限的)。为了释放内存,使用delete操作符,如下:

    delete pA;   
  • delete[] pB;

注意delete操作符并不返回任何数字,但是其仍被称作操作符,看起来它应该被叫做语句更加合适,但为了满足其依旧是操作符的特性,C++提供了一种很特殊的数字类型——void.其表示无,即什么都不是,因此delete其实是要返回数字的,只不过返回的数字类型为void罢了。
注意上面对pA和pB的释放不同,因为pA按照最开始的书写,是new unsigned long返回的,而pB是new unsigned long[ *pA ]返回的。所以需要在释放pB时在delete的后面加上“[]”以表示释放的是数组,不过在VC中,不管前者还是后者,都能正确释放内存,无需“[]”的介入以帮助编译器来正确释放内存,因为以Windows为平台而开发程序的VC是按照Windows操作系统的方式来进行内存分配的,而Windows操作系统在释放内存时,无需知道欲释放的内存块的长度,因为其已经在内部记录下来(这种说法并不准确,实际应是C运行时期库干了这些事,但其又是依赖于操作系统来干的,即其实是有两层对内存管理的包装,在此不表)。
类型修饰符(type-specifier)
类型修饰符,即对类型起修饰作用的符号,在定义变量时用于进一步指明如何操作变量对应的内存。因为一些通用操作方式,即这种操作方式对每种类型都适用,故将它们单独分离出来以方便代码的编写,就好像水果。吃苹果的果肉、吃梨的果肉,不吃苹果的皮、不吃梨的皮。这里苹果和梨都是水果的种类,相当于类型,而“XXX的果肉”、“XXX的皮”就是用于修饰苹果或梨这种类型用的,以生成一种新的类型——苹果的果肉、梨的皮,其就相当于类型修饰符。
本文所介绍的数组和指针都是类型修饰符,之前提过的引用变量的“&”也是类型修饰符。
类型修饰符只在定义变量时起作用,如前面的
  • unsigned long a, b[10], *pA = &a, &rA = a;

这里就使用了上面的三个类型修饰符——“[]”、“*”和“&”。上面的unsigned long暂且叫作原类型,表示未被类型修饰符修饰以前的类型。下面分别说明这三个类型修饰符的作用。
数组修饰符“[]”——其总是接在变量名的后面,方括号中间放一整型数c以指明数组元素的个数,以表示当前类型为原类型c个元素连续存放,长度为原类型的长度乘以c.因此long a[10];就表示a的类型是10个long类型元素连续存放,长度为10*4=40字节。而long a[10][4];就表示a是4个long[10]类型的元素连续存放,其长度为4*40=160字节。
相信已经发现,由于可以接多个“[]”,因此就有了计算顺序的关系,为什么不是10个long[4]类型的元素连续存放而是倒过来?类型修饰符的修饰顺序是从左向右进行计算的。故short *a[10];表示的是10个类型为short*的元素连续存放,长度为10*4=40字节。
指针修饰符“*”——其总是接在变量名的前面,表示当前类型为原类型的指针。故:
  • short a = 10, *pA = &a, **ppA = &pA;

注意这里的ppA被称作多级指针,即其类型为short的指针的指针,也就是short**.而short **ppA = &pA;的意思就是计算pA的地址的值,得一类型为short*的地址类型的数字,然后“&”操作符将此数字转成short*的指针类型的数字,最后赋值给变量ppA.
如果上面很昏,不用去细想,只要注意类型匹配就可以了,下面简要说明一下:假设a的地址为2000,则pA的地址为2002,ppA的地址为2006.
对于
  • pA = &a;

先计算“&a”的值,因为a等同于地址,则“&”发挥作用,直接将a的地址这个数字转成long*类型并返回,然后赋值给pA,则pA的值为2000.
对于
  • ppA = &pA;

先计算“&pA”的值,因为pA等同于地址,则“&”发挥作用,直接将pA的地址这个数字转成long**类型(因为pA已经是long*的类型了)并返回,然后赋值给ppA,则ppA的值为2002.
引用修饰符“&”——其总是接在变量名的前面,表示此变量不用分配内存以和其绑定,而在说明类型时,则不能有它,下面说明。由于表示相应变量不用分配内存以生成映射,故其不像上述两种类型修饰符,可以多次重复书写,因为没有意义。且其一定在“*”修饰符的右边,即可以
  • long **&a = ppA;

但不能

    long *&*a;  
  • 或  
  • long &**a;

因为按照从左到右的修饰符计算顺序,long*&*表示long的指针的引用的指针,引用只是告知编译器不要为变量在栈上分配内存,实际与类型无关,故引用的指针是无意义的。
而long&**则表示long的引用的指针的指针,同上,依旧无意义。同样
  • long &a[40];//错误的

因为其表示分配一块可连续存放类型为long的引用的40个元素的内存,引用只是告知编译器一些类型无关信息的一种手段,无法作为类型的一种而被实例化。
应该注意引用并不是类型(但出于方便,经常都将long的引用称作一种类型),而
  • long **&rppA = &pA;是错误的

因为上句表示的是不要给变量rppA分配内存,直接使用“=”后面的地址作为其对应的地址,而&pA返回的并不是地址类型的数字,而是指针类型,故编译器将报类型不匹配的错误。
但是即使
  • long **&rppA = pA;

也同样失败,因为long*和long**是不同的,不过由于类型的匹配,下面是可以的:

  • long a = 10, *pA = &a, **ppA = &pA, *&rpA1 = *ppA, *&rpA2 = *( ppA + 1 );
类型修饰符和原类型组合在一起以形成新的类型,如long*&、short *[34]等,都是新的类型,应注意前面new操作符中的<类型名>要求写入类型名称,则也可以写上前面的long*等,即:
  • long **ppA = new long*[45];

即动态分配一块4*45=180字节的连续内存空间,并将首地址返回给ppA.同样也就可以:

    long ***pppA = new long**[2];  
  • 而  
  • long *(*pA)[10] = new long*[20][10];

也许看起来很奇怪,其中的pA的类型为long *(*)[10],表示是一个有10个long*元素的数组的指针,而分配的内存的长度为(4*10)*20=800字节。因为数组修饰符“[]”只能放在变量名后面,而类型修饰符又总是从左朝右计算,则想说明是一个10个long元素的数组的指针就不行,因为放在左侧的“*”总是较右侧的“[]”先进行类型修饰。
故C++提出上面的语法,即将变量名用括号括起来,表示里面的类型最后修饰,
故:

    long *(a)[10];  
  • 等同于  
  • long *a[10];  


  • long *(&aa)[10] = a;

也才能够正确,否则按照前面的规则,使用
  • long *&aa[10] = a;

将报错(前面已说明原因)。而
  • long *(*pA)[10] = &a;

也就能很正常地表示我们需要的类型了。因此还可以

    long *(*&rpA)[10] = pA;  
  • 以及  
  • long *(**ppA)[10] = &pA;
希望通过本文的介绍,能够让你清楚的明白C++中的指针概念以及用法。


结帖率:100% (16/16)
发表于 2013-2-18 20:35:08 | 显示全部楼层   四川省成都市
兄弟们  发现沙发  如何处理
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表