C++指针、new、delete

指针:一种复合数据类型,和数组一样基于其他类型,指针用于存储特定变量的地址。

存储:所占内存大小取决于所使用的计算机系统,和指针存储的数据类型没有关系,之所以要确定数据类型是为了方便当前存储数据的访问。

指针与变量:指针用于存储变量的地址,可以用*间接值运算符访问当前地址内存储的数据,之前我们提到,变量在声明之后程序将自动为变量分配地址,并可以使用取地址运算符(&)来获取存储变量的内存地址,指针和变量就像是一枚硬币的正反面,使用*,&来翻转就可以看到对面的值。

int* p,a = 5;    //p是int类型指针,a是int变量
p = &a;    //*p = a = 5
*p = 6;    //a = *p = 6
  •  * 运算符:" *p "和 a 一样具有一样的权限,可以看作指针指向变量的同时给该变量起了一个别名。
  • 程序会给指针变量自动分配存储地址,在指针地址未被初始化之前指向该内存所存储的地址,该地址的值是不确定的,可能指向任意内存。所以在指针未被初始化不要对指针指向的变量进行任何操作。
  • 虽然指针存储的地址是整型,但是不能当作简单整型来操作,C语言允许整数赋值,但C++只允许指针之间的赋值。同时由于地址和底层硬件(内存)关系密切,一般为十六进制,方便内存操作。

指针的的更多操作:

指针算术

(pt = pt  +/-  n):将 pt 指针向正/反方向移动 n 个长度(sizeof(*pt)个字节)。

   int a = pt - pn  :  得到两个指向同一数组的指针之间的距离(以长度sizeof(*pt)为单位)。

#include <iostream>
using namespace std;
int main()
{
    int a = 1,b=100000;
    cout << &a << "  " << &b << endl;
    int* pt = &a;
    cout << *pt << "\t\t" << pt << endl;
    pt = pt + 1;
    cout << *pt << "\t\t" << pt << endl;
    pt = pt - 1;
    cout << *pt << "\t\t" << pt << endl;
    return 0;
}

pt [ n]:可以看作将( pt  +/-  n )为起始地址,sizeof(*pt)为长度的内存名称标记为pt [ n]。

#include <iostream>
using namespace std;
int main()
{
    int a = 5,b=8;
    int* pt = &a;
    cout << &a << " : " << &b << endl;
    pt[0] = 6;
    pt[1] = 7;
    cout << pt[0] << "\t" << pt[1] << "\t" << endl;
    cout << &pt[0] << " : " << &pt[1] << endl;
    cout << pt << endl;
    return 0;
}

#include <iostream>
using namespace std;
int main()
{
    int a[2] = {1,2};
    cout << a <<endl;
    cout << &a[0] <<endl;
    cout << &a[1] <<endl;
    cout << &a[2] <<endl;
    return 0;
}

1、指针算术和C++内部处理数组的方式决定了指针和数组名几乎有着一样的特性,不同的是指针是变量可以进行修改,而数组名不可以

  • 数组名单独出现时解释为第一个元素的地址,但使用 &a 时,a被解释为整个数组。
  • 对数组进行使用sizeof运算符时,数组名被解释为整个数组。

2、实际上编译器会将 pt [ n] 看作 *( pt + n)



 动态存储

        目前涉及到的变量内存分配方案(包括每个类型数据所需内存的大小)在编译的时候已经完成了,而C++的OOP编程实现了在运行阶段分配(new运算符)和释放内存(delete运算符)的功能。

new运算符:可以在内存中寻找符合存储条件的内存并返回其地址。

delete运算符:可以释放指定地址的内存。

  • 这里的内存指堆或者自由存储区的内存,而一般情况下变量的内存存储在栈的内存区域里。        

        new运算符可以找到适合的内存地址,之前我们通过变量名来访问这片内存区域,现在得到的内存我们该如何使用呢?使用指针变量。

	int* p = new int;
	*p = 5;
	cout << *p << ":" << p << endl;
	delete p;
	cout << p << endl;
	cout << *p << ":" << p << endl;

运行结果:

        5:00C77B98                         
        -572662307:00C77B98        

  1. 将获取的内存地址存储在指针变量中,同时这使得这片内存有了名称(*p),通过该名称实现对内存的访问;
  2. 使用后再用delete运算符释放指针p所指向的地址内存(大小由指针类型决定)。
  • 释放后指针仍然存在,可以继续使用
  • delete运算符只能释放new运算符分配的内存
  • new和delete一般成对出现,保证内存及时释放,不然容易造成内存泄漏(存储指针内存被释放,而动态分配的内存未释放,这将造成该内存空间无法访问)
  • 对于同一片内存的重复释放会造成不确定的结果


动态存储应用

        之前提到变量的内存分配会在编译阶段实现(静态联编),也可以使用new运算符在运行阶段进行内存分配(动态联编),如果运行阶段再决定是否分配内存能够有效节省内存空间。动态存储节省内存的同时也增加了编程的复杂性与不安全因素,只有当有必要节省内存(有大量大型数据:数组,字符串,结构体)时使用。

  • 动态分配的内存并不支持sizeof运算符

动态数组:数组创建后返回首个元素地址赋给指针,由于指针算术和C++内部处理数组的方式极其相似,可以直接将指针名当作数组名来对动态数组进行访问。

  • 由于指针类型和数组首地址相同,只是一个长度为一个元素的长度,只释放第一个元素的内存显然是不够的,C++提供了delete [ ] 来释放整个数组的内存。

动态字符串:字符串常量 " xxx " 实际上并不是一个数据值,而是该常量第一个元素的地址,这也解释了为什么C风格字符串只允许在初始化的时候使用常量赋值。

  • 将字符串常量复制到new的内存应该使用strcpy(x,y),strncpy(x,y,n),毕竟C风格字符串只允许在初始化的时候使用常量赋值。

动态结构体:可以使用new为已经定义好结构规范的结构类型分配空间,之前我们使用成员运算符( . )来访问结构体中的各个变量,而new的结构体内存并没有名称,为此C++提供了一个使用指针访问结构体的工具箭头成员运算符(->)。

#include <iostream>
using namespace std;
struct abc
{
    int a;
    char b;
};
int main()
{
    abc* pt = new abc;
    pt -> a = 1;
    pt -> b = 'a';
    cout << pt -> a << " : " << pt -> b << endl;
    delete pt;
    abc m = {2,'b'};
    abc* pn = &m;
    cout << pn -> a << " : " << pn -> b << endl;
    return 0;
}

简要介绍数组的替代品

模板类vector:使用动态存储的的数组类型数据,动态存储的特性让vector具有更大的自由度,可以在运行过程中通过类的方法设置、调整数组长度,在任意位置新增数据,删除数据。

模板类array:和数组一样使用静态存储,在数组的基础上增加了同类型(数据类型,长度相同)数组相互赋值,限制使用数组名访问超范围数据等方法。

vector<typeName> sz_name;    //vector<typeName> sz_name(n)
array <typeName,n> sz_name;

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值