回头再看C++【第六天】

指针

内存地址

要理解指针的含义和使用方法,首先要了解计算机的数据存储。计算机使用多个连续的二进制位来记录数据。学过数字逻辑的同学很容易理解,内存就是成组的寄存器按规律排列而成,可以简单的理解为一个表格,每个格子内保存一个二进制的0或1。按照一定的次序和组合读取这个表格,就可以由二进制数得到写入的信息。
为了准确的读取信息,我们需要对这些格子进行分组和编号,一般是相邻的八个格子为一组,也就是一个字节,然后对表格中的组编号,也就是按次序对字节编号。这个过程叫内存编址。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qLfKG7Dc-1580870952656)(en-resource://database/435:1)]

C++的指针,就是专门用来存放内存地址的数据类型。对于初学者来说可能有些难以理解,但有过一定的编程经验的同学应该能够体会到使用指针必要性。
严格地说,一个指针是一个地址,是一个常量。而一个指针变量却可以被赋予不同的指针值,是变量。但常把指针变量简称为指针。为了避免混淆,我们约定:"指针"是指地址,是常量,"指针变量"是指取值为地址的变量。定义指针的目的是为了通过指针去访问内存单元。
从指针的意义上来说,指针实际上是指向一个“数据”,一个数据结构在内存中的存储都是连续的,而依据指针指向的“数据”的首地址,可以方便的读取整个“数据”。

指针的定义

在C++语言中,定义指针的方式如下:

<类型名>* <变量名>
//例如
int* pInt;
string* pString;
float* pFloat;
double* p1, p2;

也有这样的写法:

<类型名> *<变量名>

但不太推荐。相比于后者,前一种写法能够更直观的说明“变量名”是一个指针,当然,这只是笔者个人的习惯,不必拘泥于此。

识别指针

大部分时候可以使用sizeof运算符来获取变量的长度,再依据长度判断某个变量是指针还是其他数据类型。

指针的分类

一般按照指针指向的对象分类,如果指针指向的对象也为指针,可以按级划分,按照此原则,C++指针可以分为单级间指针和多级间指针。单级间指针直接指向对象,多级 间指针仍然指向指针。指针的级数由指针定义时的指针标识符表示,每出现一个*就增加一级。

指针初始化

指针被定义后,仅依据其指向类型分配了一个内存单元,而没有对指针进行初始化处理。如果没有初始化,那么指针的指向是随机的、未知的,即野指针。直接引用野指针可能回破坏程序的运行,甚至影响到操作系统的安全。使用指针前必须进行初始化。一般有两种方法。

  • 给指针赋空值:赋予数值0或NULL。
  • 内存地址:使指针指向某个特定的地址。具体如下:
  1. 可以使用一个已经被初始化的指针地址来初始化一个指针,之后两个指针将会指向相同的单元。如果两个指针的类型相同,可以直接赋值;否则应转换为与被初始化指针相同类型的指针。
  2. 可以使用已经定义的变量来初始化指针,此时需要用到取地址运算符&。
指针= & 变量;

同样需要指针和变量对应的类型相同。
3. new分配内存单元

指针 = new 类型名;
指针 = new 类型名[n];

其中,[n]表示需要n个"类型名"长度的存储单元;new返回新分配的内存单元地址。当申请完内存单元后,如果不再需要就要收回这个内存单元,此时需要delete运算符来完成。

delete 指针名;
delete []指针名;
  1. malloc函数:
    由c中继承来的方法。使用malloc/free对来分配和释放动态内存,
extern void* malloc(unsigned int num_bytes);

此函数在头文件malloc.h中,其功能是申请nums_bytes字节的连续内存块。如果申请成功则返回该块的首地址;否则返回空指针NULL。

type* p;
p = (type*)malloc(sizeof(type)* n);

p是type型指针,sizeof(type)是计算一个type型数据需要的字节数,n表示需要存储n个type型数据。(type*)是对malloc的返回值进行强制转换。该式的含义是申请可以存储n个type型数据的内存块,并且将块的首地址转换为type型赋给p。
当用malloc分配的内存不再使用时,应使用free()函数将内存块释放。

free p;

同delete一样,free也没有破坏指针p的内容,只是告诉系统收回这片内存单元,可以重新利用。所以free后,最好将p显式置空指针。

指针运算

指针作为变量,可以进行运算处理,但c++指针的运算种类很有限,且变化规律受其所指向的类型的制约。

算术运算

指针只能进行加减两种算术运算。指针的加减变化规律要受所指向的类型约束。它只能与以整型作为基类型的数据 类型进行运算,或者在指针变量之间。指针的加减运算不是单纯的在原地址基础上加减1,而是加减一个数据类型的长度。所以,指针运算中"1"的意义随数据类型的不同而不同。
尤其要注意的是指针的运算要限定在事先申请号的内存单元内,最近在做leetcode题目时常遇到越界错误,就是忽略了这一点。
两指针之间只能进行减运算。两个指针的减法表示计算它们之间的元素个数。如果差为负 数,表示地址高的指针需要后移几次才能到地址低的指针处。如果是正数,表示地址低的 指针需要移动几次才能前进到地址高的指针处。这个值实际是指针地址的算术差除以类型 宽度得到的。

关系运算

指针的关系运算是比较地址间的关系,这包括两方面:一方面是判断指针是否为空;另一 方面是比较指针的相对位置。进行关系运算的两个指针必须指向相同的数据类型
p1==p2:判断p1和p2是否指向同一个内存地址。
p1>p2:判断p1是否处于比p2高的高地址内存位置。
p1>=p2:判断p1是否处于不低于p2的内存位置。
p1<p2:判断p1是否处于比p2低的低地址内存位置。
p1<=p2:判断p1是否处于不高于p2的内存位置。
p1==0/p1==NULL:判断p1是否是空指针,即什么都不指。
p1!=0/p1!=NULL:判断p1是否 不是空指针,即指向某个特定地址。

指针的指针

即指向指针的指针,又称二级间指:

type** p;

按这样的方式可以定义n级间指,只需在类型名后加上对应数量的‘*\’号即可。

指针赋值

很少直接把地址值赋给指针,一般都是利用已定义的变量完成

p1 = &var;      //把var的地址赋给p1
p1 =  p2;       //把p2的值赋给p1
*p1 = var;      //把var放进p1指向的地址

‘*’和‘&’

*操作符也叫间接访问运算符,用来表示指针所指的变量,结合性为从右到左,属于单目运算。*运算符后跟的必须是指针变量。如果作为左值,则是向指针所指单元中写入数据。如果作为右值,则是从指针所指单元中读数据。
&操作符是取地址运算符,表示变量的地址值。可以用于一般变量也可用于指针变量。

指针和引用

引用就是别名或同义词,它是同一块内存单元的不同名称。常用于替代传值方式,传递参 数和返回值。具有指针的特点,可节省内存复制带来的开销。

type &ref=var;

type是类型名称,&在此不是求地址运算,而是起标识作用,ref是引用的名称,var是与引用同类型的变量名称。容易发现,对ref和var使用&运算,得到的地址值是一样的,即两个变量使用的是同一块内存空间。
关于引用的详细内容可以参考引用的用法

特殊指针

void型指针

无类型指针,没有类型,只是指向一块申请号的内存单元

void* p;

定义了一个指针p,但未规定应该按何种格式来解释其指向的内存单元的内容。p本身的存储空间是定义时就申请好的,但p指向的空间可以到需要时再申请。
如下语句

void x;     //不被允许

void只是说明被定义变量的类型,而不涉及内存的分配。
在malloc()函数的声明格式中,返回值就为void型。这表明malloc只是按照要求的大小分配了内存单元,不负责解释这些内存单元的格式。因此,在使用malloc时,一定要用强制类 型转换,转换为需要的类型。

空指针

空指针就是什么都不指的指针,一般赋0值或NULL值。常用于指针的初始化。需要注意的是,空指针不能进行取值(*)操作。

  • void型指针是无类型指针,它只是说明还没有对被指向的内存单元进行格式化解释。
  • 野指针表示指针声明后没有初始化,没有指向特定的内存单元。
  • 空悬指针表示指针指向的内存单元被释放了,该指针可能指向任何地方,也可能还指向原单元。
  • 空指针则是指指针什么都不指。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值