C++基础之指针的详细介绍(二)

开发 后端
本篇说明C++中的重中又重的关键——指针类型,并说明两个很有意义的概念——静态和动态。希望对你有帮助,一起来看。

本篇是上一篇的续篇,接着为大家介绍C++中的指针

在堆上分配内存

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

  1. unsigned long *pA = new unsigned long;   
  2. *pA = 10;  
  3. unsigned long *pB = new unsigned long[ *pA ]; 

上面就申请了两块内存,pA所指的内存(即pA的值所对应的内存)是4字节大小,而pB所指的内存是4*10=40字节大小。应该注意,由于new是一个操作符,其结构为new <类型名>[<整型数字>].它返回指针类型的数字,其中的<类型名>指明了什么样的指针类型,而后面方括号的作用和定义数组时一样,用于指明元素的个数,但其返回的并不是数组类型,而是指针类型。

应该注意上面的new操作符是向操作系统申请内存,并不是分配内存,即其是有可能失败的。当内存不足或其他原因时,new有可能返回数值为0的指针类型的数字以表示内存分配失败。即可如下检测内存是否分配成功。

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

 

上面的if是判断语句,下篇将介绍。如果pA为0,则!pA的逻辑取反就是非零,故为逻辑真,进而执行相应的工作。

只要分配了内存就需要释放内存,这虽然不是必须的,但是作为程序员,它是一个良好习惯(资源是有限的)。为了释放内存,使用delete操作符,如下:

  1. delete pA;   
  2. 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的皮”就是用于修饰苹果或梨这种类型用的,以生成一种新的类型——苹果的果肉、梨的皮,其就相当于类型修饰符。

本文所介绍的数组和指针都是类型修饰符,之前提过的引用变量的“&”也是类型修饰符。

类型修饰符只在定义变量时起作用,如前面的

  1. 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字节。

指针修饰符“*”——其总是接在变量名的前面,表示当前类型为原类型的指针。故:

  1. short a = 10, *pA = &a, **ppA = &pA; 

 

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

对于

  1. pA = &a; 

 

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

对于

  1. ppA = &pA; 

 

先计算“&pA”的值,因为pA等同于地址,则“&”发挥作用,直接将pA的地址这个数字转成long**类型(因为pA已经是long*的类型了)并返回,然后赋值给ppA,则ppA的值为2002.

引用修饰符“&”——其总是接在变量名的前面,表示此变量不用分配内存以和其绑定,而在说明类型时,则不能有它,下面说明。由于表示相应变量不用分配内存以生成映射,故其不像上述两种类型修饰符,可以多次重复书写,因为没有意义。且其一定在“*”修饰符的右边,即可以

  1. long **&a = ppA; 

 

但不能

  1. long *&*a;  
  2. 或  
  3. long &**a; 

 

因为按照从左到右的修饰符计算顺序,long*&*表示long的指针的引用的指针,引用只是告知编译器不要为变量在栈上分配内存,实际与类型无关,故引用的指针是无意义的。

而long&**则表示long的引用的指针的指针,同上,依旧无意义。同样

  1. long &a[40];//错误的 

 

因为其表示分配一块可连续存放类型为long的引用的40个元素的内存,引用只是告知编译器一些类型无关信息的一种手段,无法作为类型的一种而被实例化。

应该注意引用并不是类型(但出于方便,经常都将long的引用称作一种类型),而

  1. long **&rppA = &pA;是错误的 

 

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

但是即使

  1. long **&rppA = pA; 

 

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

 

  1. long a = 10, *pA = &a, **ppA = &pA, *&rpA1 = *ppA, *&rpA2 = *( ppA + 1 ); 

类型修饰符和原类型组合在一起以形成新的类型,如long*&、short *[34]等,都是新的类型,应注意前面new操作符中的<类型名>要求写入类型名称,则也可以写上前面的long*等,即:

  1. long **ppA = new long*[45]; 

 

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

  1. long ***pppA = new long**[2];  
  2. 而  
  3. long *(*pA)[10] = new long*[20][10]; 

 

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

故C++提出上面的语法,即将变量名用括号括起来,表示里面的类型最后修饰,

故:

  1. long *(a)[10];  
  2. 等同于  
  3. long *a[10];  

 

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

 

也才能够正确,否则按照前面的规则,使用

  1. long *&aa[10] = a; 

 

将报错(前面已说明原因)。而

  1. long *(*pA)[10] = &a; 

 

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

  1. long *(*&rpA)[10] = pA;  
  2. 以及  
  3. long *(**ppA)[10] = &pA; 

希望通过本文的介绍,能够让你清楚的明白C++中的指针概念以及用法。

责任编辑:于铁 来源: 互联网
相关推荐

2011-07-14 17:02:09

C++指针

2011-07-14 16:56:21

2011-07-14 23:27:05

C++引用

2011-07-20 16:43:34

C++

2011-07-15 01:38:56

C++this指针

2011-06-21 10:37:56

const

2011-07-14 16:26:01

2011-07-20 14:12:48

2011-07-15 10:08:11

C++运算符重载

2011-07-13 11:12:43

C++MFC

2011-07-20 18:03:54

CC++

2010-02-01 10:22:51

C++数据指针

2011-07-13 16:14:53

C++引用指针

2009-08-21 15:16:23

C#使用指针

2011-07-15 01:29:39

C++析构函数

2011-07-15 01:20:58

C指针函数函数指针

2011-07-20 15:58:53

C++引用

2011-07-13 16:49:59

C++

2010-01-28 13:57:19

C++指针基础

2015-05-13 10:37:58

C++指针与引用
点赞
收藏

51CTO技术栈公众号