什么是指针?
- 指针(pointer)是"指向(point to)"另外一种类型的复合类型。
- 定义指针类型的方法:在变量类型后、变量名之前加
*
,即可 - 可以理解为
*
属于类型的一部分!
获取对象的地址
- 指针存放某个对象的地址,用取址操作符(
&
)获取地址 -
&
操作符还有其他作用,视上下文而定!
double dv;
double *pd0 = &dv;
double *pd1 = pd0;
int *pi = pd0; // 错误示例:指针pi的类型和pd的类型不匹配
pi = &dv; // 错误示例:试图把double型对象的地址赋给int型指针
指针值
指针的值(即地址)有4种状态:
- 指向一个对象
- 指向紧邻对象所占空间的下一个位置
这里很多同学可能不是特别理解,指针指向对象,为啥指向相邻空间的位置?这个可以参考数组去理解,数组可以当作指针理解,
数组名
就是指针名
并指向第一个数据,那数组名+1...+n
,就指向数组第n个数据!
- 空指针,意味着指针没有指向任何对象
- 无效指针
容易发现错误的情况就是拷贝或以其他方式访问无效指针,编译器不负责检查此类错误,访问无效指针的后果很严重!
通过指针如何访问对象
- 指针指向了一个对象,则可以使用解引用符(
*
)来访问该对象 -
*
操作符还有其他作用,是视上下文而定! - 对指针解引用会得到所指的对象,如果给解引用的结果赋值,就是给指针所指的对象赋值
int iv = 8;
int *pi = &ivl; // pi 是指向变量 iv 的指针
cout << *pi ; // *pi 得到指针pi指向的对象,输出 8
*pi = 80; // *pi 得到指针pi指向的对象,可由 *pi 为变量 iv 赋值
cout << *pi; // 输出 80
cout << iv; // 输出 80
什么是空指针
- 空指针不指向任何对象,使用一个指针前可以先判空
- 获取空指针最直接的方法就是使用字面值nullptr。
- 直接把 int 变量赋值给指针是错误的操作
int *p1 = nullptr; // 创建空指针
int *p2 = 0; //错误:不能直接赋值!虽然不会提示语法错误,但是不能这么写!
赋值和指针
- 问:赋值语句是改变了指针的值还是改变了指针所指对象的值?
答:赋值语句永远改变的是等号左侧的对象的值
int iv1 = 1;
int iv2 = 2;
int *p = &iv1;
*p = iv2; //改变的是 指针p 所指对象的值
p = &iv2; //指针p 被改变,指向 iv2 的地址
指针比较
可以用相等操作符(==)或者不相等操作符(!=)进行比较,比较结果是布尔类型的。如果两个指针存放的地址值相同,则其相等;反之,不相等
void* 指针
- void是一种特殊的指针类型,可用于存放任意对象的地址。一个void 指针存放着一个地址,但是该地址中存放的是什么类型的对象并不透明。
- void指针的功能有限:可以作为函数的输入或输出,或者赋给另外一个void指针;不能直接操作void*指针所指的对象。
指针的转换
- 数组自动转换成指向数组首元素的指针
- nullptr能转换成任意指针类型
- 常量整数值0能转换成任意指针类型
- 指向任意非常量的指针能转换成void*
- 指向任意对象的指针能转换成 const void*
指向指针的指针
- 一般来说,声明符中修饰符的个数没有限制,当有多个修饰符连写在一起时,按照其逻辑关系详加解释即可。
- 指针是内存中的对象,有自己的地址,允许把指针的地址再存放到另一个指针中。
- 通过*的个数可以区分指针的级别,即为
**
表示指向指针的指针,***
表示指向指针的指针的指针 -
问:何时用到指向指针的指针?
答:常用的一个场景:当一个函数接收一个指针A(地址A),函数内部只能修改这个指针A指向的对象,不能修改这个指针A指向地址B,如果我们想修改这个指针A指向地址B怎么办?就需要传递指针B(指针A的地址),这个时候我们就可以修改指针A的指向了,指针B就是指向指针的指针
指针和const
- 常量指针,指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用常量指针。
const int dv = 2; // dv是常量,值不能改变
int *pd = &dv; // 错误:pd 是一个普通指针
const int *cpd = &dv; // 正确:cpd 可以指向一个常量
int dv2 = 3;
cpd = &dv2 ; // 正确:但是不能通过cpd 改变 dv 的值 可以通过其他途径修改dval的值
- const指针(指针常量)
指针常量(const pointer)必须初始化,而且指针一旦初始化,指针的值(存放在指针中的地址)就不允许改变。
int i = 0;
int *const ptr = &i;
指针和数组
- 指针和数组关系密切。
- 使用取地址符&可以获取某个对象的指针,取地址符可以用于任何对象。
- 数组的元素也是对象,对数组使用下标运算符得到该数组指定位置的元素。对数组的元素使用取地址符能得到指向该元素的指针。
- 在用到数组名字的地方,很多时候编译器会自动地把数组替换为一个指向数组首元素的指针
数组名被解释为指向该数组的第一个元素的指针,但以下情况除外:
1.当作为sizeof运算符及typeid运算符的操作数时
2.当作为取地址符的操作数时 ("&数组名" 是指向数组整体的指针,而不是指向"指向第一个元素的指针"的指针。)
3.当使用decltype关键字时,返回的类型是数组而非指针。
- 很多情况下使用数组类型的对象其实是使用一个指向该数组首元素的指针
指针允许递增递减
- vector和string的迭代器支持的运算,数组的指针全部支持。
- 允许使用递增、递减运算符将指向数组元素的指针向前移动到下一个、上一个位置,超过边界时不能解引用和递增(尾后指针)
- 标准库函数begin和end
尾后指针比较容易失误,非常危险,C++11引入了begin和end函数,这两个函数和容器中的两个同名成员函数功能类似,但是数组不是类类型,因此这两个函数不是成员函数。使用方法如下
int ia[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *beg = begin(ia); // 指向ia的首元素的指针
int *last = end(ia); // 指向ia尾元素的下一位置的指针
网友评论