C++指针和自由存储空间

计算机程序在存储数据时必须跟踪3个基本属性:

  • 信息存储在何处
  • 存储的值是多少
  • 存储的信息是什么类型

我们可以从C++ 语法的角度简单分析一下声明语句;

int a;

该声明语句指出了值的类型和符号名,还让程序为值分配内存,并在内部跟踪该内存单元,这是一种基本类型的策略。

我们来看复合类型的策略。即指针的策略,指针是一个变量,其存储的是值的地址,而不是值本身。对于两种不同变量的指针,虽然这两个指针指向不同的数据类型,但是其本身的值(即地址)的大小是相同的,通常在32位的机器上,一个指针占4字节,因此求sizeof得到4;在64位的机器上,一个指针占8字节,因此求sizeof得到8.

指针大小准确滴说跟你用的编译器直接相关,那个应该看编译器手册,什么平台环境只要编译器支持都可以,比如你用64位系统,而你用的编译器的设定的指针是32位的,那你用那个编译器写程序的时候指针也是32位的。一般指针在处理器处理器上面就一个间接寻址或者跳转,而有些处理器间接寻址空间是有设定的,跳转也分长短跳转,而且设置还有指令长度模式设定等等,而指针那个概念只会在高级语言里面的设定,具体是长跳转还是短跳转,寻址模式,使用指令长度模式等等设置都会在编译器里面搞,写程序的完全不关心而已。而原理上编译器在一个16位处理器弄个32位的指针是完全木有问题的,但木有实质的意义。

使用new分配内存

变量是在编译时分配的有名称的内存,而指针指示为可以通过名称直接访问的内存提供了一个别名。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。C语言中可以使用 mallco 来分配内存, C++ 中可以使用 new。

int * pn = new int

上述语句中,new int 告诉程序,需要适合存储int 的内存,new 运算符根据类型来确定需要多少字节的内存,然后,它找到对应的内存,并返回该内存块的地址。接下来,将地址赋给pn。
我们在这里放上一段CPP primer plus 的示例代码:

#include <iostream>
int main() {
    using namespace std;

    int nights = 1001;
    // allocate space for an int
    int* pt = new int;
    // store a value there
    *pt = 1001;

    cout << "nights value = ";
    cout << nights << ": location " << &nights << endl;
    cout << "int ";
    cout << "value = " << *pt << ": location = " << pt << endl;
    double * pd = new double;   // allocate space for a double
    *pd = 10000001.0;           // store a double there

    cout << "double ";
    cout << "value = " << *pd << "; location = " << pd << endl;
    cout << "location of pointer pd: " << &pd << endl;
    cout << "size of pt = " << sizeof(pt);
    cout << "; size of *pt = " << sizeof(*pt) << endl;
    cout << "size of pd = " << sizeof pd;
    cout << "; size of *pd = " << sizeof(*pd) << endl;
    return 0;
}

// output
nights value = 1001: location 0x7ffee3fac938
int value = 1001: location = 0x7f9b83405930
double value = 1e+07; location = 0x7f9b83405940
location of pointer pd: 0x7ffee3fac928
size of pt = 8; size of *pt = 4
size of pd = 8; size of *pd = 8

上述代码中需要注明几点,

  • 声明指针时必须指出指针所指向的类型,因为指针存储了数据对象的地址信息,而地址本身只指明了对象存储地址的开始,而没有指出其类型(使用的字节数)。由于上述代码中知名了指针的类型,因此打印 *pt 的值时 cout 知道要读取多少字节以及如何解释他们。
  • new 分配的内存块通常与常规变量声明分配的内存块不同。变量nights和pt的值都被存储在栈中,而 new 从被称为堆(heap)或自由存储区(free store)都内存区域分配内存。
  • 在内存被耗尽的情况下,new 或引发异常,在较老的C++实现中,new会返回0(null pointer),即空指针,C++确保空指针不会指向有效的数据

Delete

#include <iostream>
int main() {
    using namespace std;
    int* pt;
    *pt = 10;
    cout << "==========before delete ============" << endl;
    // int value = 10; location = 0x7ffee341d948
    cout << "int ";
    cout << "value = " << *pt << "; location = " << pt << endl;
    // location of pointer pt: 0x7ffee341d920
    cout << "location of pointer pt: " << &pt << endl;
    delete pt;
    // ERROR
    // delete pt;
    cout << "==========after delete ============" << endl;
    cout << "int ";
    // ERROR malloc: *** error for object 0x7ffee64d2948: pointer being freed was not allocated
    cout << "value = " << *pt << "; location = " << pt << endl;
    // ERROR malloc: *** error for object 0x7ffee341d948: pointer being freed was not allocated
    cout << "location of pointer pt: " << &pt << endl;
}

这将释放ps指向的内存,但不会删除ps指针本身。例如,可以将ps重新指向另一个新分配的内存块。一定要配对地使用 new 和 delete。否则将出现内存泄漏(memory leak),直至内存溢出。需要注意的是,这里要注意的是:

  • 不建议使用 delete 删除栈数据,有些编译器可能会报错,如果没报错,那么下次使用该数据时就会报错
  • delete 删除 new 生成的对象时只会将该段内存标为可用,原有的数据不会进行清空,当下一次对该内存块写入新数据时原有的数据才会被抹去。
  • 使用 delete 的关键在于,将它用于 new 分配的内存。这并不意味着要使用用于 new 的指针,而是用于new 的地址,看下面代码可以帮助理解
int* a = new int;
int* b = a;
delete b;
cout << "Delete use second pointer OK" << endl;
// ERROR malloc: *** error for object 0x7fef8f405930: pointer being freed was not allocated
// delete a;

上述代码a, b都指向同一个内存块,因此使用 delete b 也可以将该内存块进行释放,即使用 delete 的关键在于使用内存块的地址,但是不要创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次的可能性。

new 和 delete 总结

当我们使用 new 和 delete 时需要遵守以下规则:

  • 不要使用 delete 来释放不是 new 分配的内存
  • 不要使用 delete 释放同一个内存块两次
  • 如果使用 new[] 为数组分配内存,则应使用 delete[] 来释放
  • 如果使用 new 为一个实体分配内存,则应使用 delete 来释放
  • 对空指针应用 delete 是安全的

Reference

  1. C++ 地址(指针)大小
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值