C++:C++primer plus 指针问题理解

来源:互联网 发布:北斗tv有网络电视版吗 编辑:程序博客网 时间:2024/06/11 02:03
看完了第四章总觉得没有练习的情况下掌握的并不是太牢固。所以选择博客的方式记录下来,一来加深印象,二来可以在以后自己没事再翻翻,还能改进一下自己的认知。

  -------------------------------------------------------------

 

指针和自用存储空间、指针数组和指针算术

 

   就指针而言,它是一种特殊的数据类型,指向数据元素的地址,本身存储的就是地址,数据元素的值才是指针变量的派生量。而对于指针变量的声明和初始化,我们略过。

   &常规变量=指针变量  两者都是指的是地址。另,就指针变量而言,作为一钟特殊的数据类型,地址才是指针变量指定存储的量,而将改地址指向的值作为派生量,这点与常规变量的存储以及常规变量用&取址正好相反。故而有 常规变量=*指针变量 两者都是指数据本身,只是两种不用的表达,同一枚硬币的两面而已。

    而说到指针以及指针指向的值得存储,总会谈及指针策略--C++内存管理编辑理念的核心。OOP和传统的过程性编辑的区别在于,OOP强调的是在程序的运行阶段,根据具体的运行情况调整分配。而编译阶段决策更像是无论那种情况都坚持按照计划执行。总之使用OOP时便可以在程序运行阶段时在开辟所需要的内存空间。对于C++使用的方法是,使用关键字new请求正确的内存空间以及使用指针来跟踪新分配的内存地址。

   typeName* pointer_name = new typeName 使用new开辟一种变量新的内存空间的时候需要同时声明指针来指向、跟踪这个地址,否则我们无法访问这些内存单元;另外地址本身只指出了对象储存地址的开始,并没有指出其数据类型即数据存储占用的字节数,只有用*指针的方法才会知道该数据的字节长度从而知道数据类型。另外需要指出的是new分配的内存块通常从堆或者自由存储区来分配,而常规变量以及指针指向的值存储的地方则成为栈,稍后具体讨论。还需要提及的是,对于new分配的空间,最后需要用delete 指针的方式来释放掉。

    然后本段用一个实例来说明如何用new来创建一个动态数组以及其使用方法。比如我们需要创建一个包含10个int元素的数组,则 int * psome =new int[10]。new将返回第一个元素的地址,同时释放时需要用delete [] psome的格式,方括号表示的是释放整个数组而不是指针指向的元素。需要注意的是:

不要使用delete释放不是new分配的内存。

不要使用delete释放同一个内存两次。

如果使用new[]为数组分配内存则用delete[]释放。

如果使用new[]为一个实体分配内存则应使用delete释放。

对于空指针,应用delete是安全的。

为数组分配内存的通用格式即 type_name * pointer_name =new type_name [num_elements]。指针指向第一个元素,然后我们可以用数组名的方式来使用指针。对于刚才例子,假设int占四个字节,则将指针移沿着正确的方向移动四个字节就会指向第二个元素,一共十个元素即指针的移动范围。那么第一个元素就是*psome。而只需将指针当做数组名就可以依次访问各个元素,当然此时第一个元素是psome[0]而不是*psome,第二个元素就是psome[1],以此类推。可以这么做的原因是C和C++内部都使用指针来处理数组,数组和指针基本等价,这也是C和C++的优点之一,我们再来看指针和数组的区别。

我们知道如果用int * p3 = new int [3]可以开辟一个用指针*p3来跟踪的长度为3的数组。我们将p3当做数组名来用,p3[0]是第一个元素,而以下代码运算以后p3=p3+1 ,p3[0]将指向第二个元素。原因是我们不能改变数组名的值,但是指针是变量,所以我们可以改变指针的值。然后我们减一以后便会指向原来的值,程序就可以用delete[]释放掉正确的空间地址。相邻的int地址通常相差2或4个字节,将指针加一以后,指针就会指向下一个元素的地址,这跟我们刚才说的指针移动一致,指针算术有一些特别的地方。

指针和数组基本等价的原因就在于指针算术以及C++内部处理数组的方式。对于整数变量,加一以后该变量的值将加一;但是将指针变量加一以后,增加的量是它指向的类型的字节数。如将指向double的指针加一后,如果系统对double使用8个字节存储,则数值将增加8。这个现象还说明了C++将数组名解释为地址,正如我们通常认为的,数组名是数组第一个元素的地址。而事实上,我们使用数组名表示数组时,C++都会执行arrayname[i] becomes *(arrayname + i),而如果使用指针表示,C++也会执行同样的转换pointername[i] becomes *(pointername + i)。因此在很多地方可以用同样的方法使用数组名和指针名。很多表达式中他们都表达地址,而区别在于指针变量的值可以被改变。同时指针和数组还有第二个区别,用sizeof函数对数组而言求得是数组的长度,而对指针应用则求的是指针的长度。

另外,数组的地址还有一些需要注意的地方。比如数组名被结束为第一个元素的地址,但是对于数组名应用取址运算符时得到的是整个数组的地址。如声明一个short tell[10];那么&tell[0]就是第一个元素,一个两字节内存块的地址,&tell是一个20字节的内存块的地址。因此表达式tell+1是将地址加2,&tell+1是将地址加20。换句话说,tell是一个*short,而&tell指向包含20个元素的short数组short(*)[20] 。同时我们可以用short(*p)[20]=&tell的方法声明一个指向含有20个元素的数组的指针,如果声明时去掉括号,声明的将是一个指针数组而不是指向数组的指针。因此p的类型是short (*)[20],另外由于p被设置为&tell。所以*p与tell等价,所以(*P)[0]是tell数组的第一个元素。

同时,数组和指针的特殊关系可以扩展到C风格的字符串。在C++中,用括号引起的字符串像数组名一样,也是第一个元素的地址,如果用cout输出,cout认定的是该字符串首元素的地址,则它将从该字符开始打印直到遇到空字符为止。只发送字符串的地址,意味着对于数组中的字符串、用引号括起的字符串常量以及指针所描述的字符串都将传递他们的地址。一般而言,cout一个指针将输出一个地址,而如果指针类型是char*则将显示指向的字符串,如果想要显示字符串的地址,就必须将指针强制转换为另一种指针类型,如int*(指针char*只显示作为字符型的对应的地址,转换为整型可以理解为整型数组,输出的是整型指针对应的数据元素)。另外,还有库函数strcpy(a,b)来将b数组复制到目标数组a中。

在运行时创建数组开辟新的内存空间优于编译时创建数组,对于结构同样如此,所以使用new创建动态结构会有更多好处。将new用于结构主要有两步:创建结构、访问成员。

struct  inflatable

{

    char name[20];

    float volume;

    double price;

};

inflatable * p = new inflatable;

即使用黑体加粗部分语句来声明一个结构体对象。因为此时结构体对象并没有名称只是使用地址使用,所以不能用成员运算符句点引用成员,需要用箭头成员运算符(->);另外一种访问方法是p是指向结构的指针,*p就是被指向的值即结构对象本身,所以(*p).price就是该结构的price成员。最后需要直接用delete p;语句释放new开辟的空间。

最后简单说明c++的分配内存方法。

1.自动存储

          在函数内部定义的常规变量使用自动存储空间,被称为自动变量。这意味着他们在所属的函数被调用时自动产生,在函数结束时消亡。即自动变量是局部变量,其作用域为包含他的代码块。自动变量通常存储于栈中,这意味着执行代码块时其中的变量将依次加入的栈中,而在离开代码块时将按照相反顺序释放变量,即后进先出。因此在程序执行过程中,栈将不断地增大和缩小。

2.静态存储

       静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一是在函数外面定义它;另一种是使用关键字static。自动存储和静态存储的关键在于:这些方法严格的限制了变量的寿命。变量可能存在与成语的整个生命周期(静态变量),也可能知识在特定的函数被执行时存在(自动变量)。

3.动态存储

       new和delete运算符提供了一种更加灵活的方法。他们管理了一个内存池----被称为自由存储空间或者堆。堆与用于静态变量和自动变量的内存是分开的。new和delete可以使变量在一个函数中分配内存,在另一个函数中释放,因此数据的生命周期完全不受程序或者函数的生存时间控制。使用new和delete使对于内存空间的使用有更大的主动权,但是内存管理更加复杂。在栈中,自动添加或者删除机制使得占用的内存总是连续的,但new和delete的相互影响可能导致占用的内存存储区不连续,跟踪新分配的内存位置更加困难。

0 0
原创粉丝点击