博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c++中指针和引用注意点整理
阅读量:4221 次
发布时间:2019-05-26

本文共 3528 字,大约阅读时间需要 11 分钟。

引用和指针是什么就大抵不介绍了,这里作者集合了一些常遇到的相关问题以及需要注意的点

首先是知乎上的一个问题,原问题如下所示:

int a[3][4];int (*p)[4] = a;p = &a[2]; // 这里为什么需要取地址符?C++中a[2]不就是下级数组名吗?为什么还要取地址?

根据这个问题,我发现了自己对指针和引用还有数组实质的理解还是有点模糊,比如问题中提到的:a[2]不就是下级数组名吗?我没法确切给出答案,因为我也不确定。这个问题下面讨论会再提到。

先来看看这几个等式为什么成立:
int a[3][4] ,这是一个我们印象中3行4列的二维数组,用gcc的typeid().name()函数 显示类型表示是A2_A3_i;;int (*p) [4]表示的是一个指向一些一维数组(4个int型)指针p,也就是p指向的数据必须是一些长度固定为4的一维数组,表示为PA3_i。见如下代码

#include
#include
#include
#include
using namespace std;int main(void){ int a[3][4] = {
1,2,3,4,5,6,7,8,9,10,11,12}; //int** b = a; //int** 无法被覆盖为int(2)(3) int (*b)[4] = &a[2]; int (*c)[4] = a; cout<<"a的类型 "<
<

1.这两者的类型我们从上面可以看出来并不是一样的,为什么可以挂上等号呢?

这里提到一个指针转换的规则,c++内部自定的隐式转换:
方便理解,我直接把翻译visual studio 2015的官方文档示例粘了张图片拿过来
这里写图片描述
看图片中这个例子,我们大概知道,c++的数组会自动转换成指向第一个元素的指针,而且这是大部分情况下,也就是说,尽管我们定义了一个type a[N] 类型的数组对象,但是在编译器大部分时候认识它的形式是将其看成一个指针,比如在执行以下操作时候:
a+1 ——指针
*a ——指针
a[0],a[1]….. ——指针
&a ——非指针
也就是说,除了&符号,其他都是将a这个对象看做指针来使用(注意是a这个对象)
再来解释上面的问题,使用数组a时候,a被隐式转换成了指向第一个元素的指针,每一个元素是int[4]型,所以最后成了指向int[4]型的指针,也就是int(*)[4],和c的类型相等了,那么对其进行初始化就没什么难理解了。

2.解释第二个问题,为什么要对a[2]取地址符号?

int main(void){    int a[3][4];    int (*p)[4] = a;    p = &a[2];    cout<<"c的类型        "<
<

看输出结果,a[2]的类型是A4_i,这个表示的意思是:存放着4个int型元素的数组(array)类型。加上自动转换a[2]就是一个int*型。

这时候就派上&的作用了,数组遇上&符号不会自动转换成指针,这时候的类型就是在&作用下变成了一个int(*)[4]的指针。(我的理解是&对这个a[2]整个元素(一个元素是一个int[4]的数组)取地址,成了一个整体,再将地址赋值给一个对等的类型,就像最简单的int *p = &a; 这个&a也就变成了一个指针,等价于左边的指针

3.解释最后一个问题,a[2]是下级数组名字吗?

错误,数组只有一个名字,我们使用a[2]是等同是用[ ]运算符对a这个惟一数组名做了偏移操作,将指针移到第三个元素的开头,实质上并没有什么所谓的下级数组名。一句话,你想怎么叫它就怎么叫它。
而且在内存中,并没有所谓的多维数组,所有变量都只是存在于一个平行的内存上,这个多维只是我们将地址一次偏移多少,将这个偏移中取出来的数据看做成了一个一维数组,多个这样的偏移成了二维数组,要继续将其分成更小的偏移量存取,那就是更多维了。这只是我们脑子里面构建出的一个模型。

上面的三个问题完全明白了,也就大致知道数组指针以及取地址符号&的概念了。还有一些需要注意的问题

4.区分 int(*ptr)[4] 和 int *ptr[4],带括号和不带括号的ptr.

#include
#include
#include
#include
using namespace std;int main(void){ int a[3][4] = {
1,2,3,4,5,6,7,8,9,10,11,12}; int b[4] = {
1,2,3,4}; int e[2] = {
1,2}; int(*ptr)[4] = a; int* ptr1[4] = {b,b,e,e}; //这个表示4个int*型的元素 cout<<"ptr的类型是 "<
<

很明显,我们可看到,ptr是一个指向A4_i类型的指针

ptr1是一个包含4个指针元素的数组,另外提一句,代码中ptr1中的4个数组名字b b e e都经过了隐式指针转换成了int*型。

5.char型指针数组

#include
#include
#include
#include
using namespace std;int main(void){ //char* line[5]; char(*line2)[5]; char b[5] = "abcd"; char *line[5] = {
"aaaa","bbbbb","ccccc","dddd","eeee"}; //**这一段是初始化,赋值的话需要单独对一个个指针进行赋值就像下面这样** /* line[0] = "aaa"; line[1] = "bbb"; line[2] = "ccc"; line[3] = "ddd"; line[4] = "eee"; */ line2 = &b; //line2只支持长度为5的char数组对其进行赋值 cout<<"line "<
<

这里可以推得和上面的ptr类似,上面的line是5个指向char数组的的指针数组,但是注意一点,这里的line2是一个指向一个长度为5的char型数组的指针。而且仅仅只能是长度为5的char型数组才能赋值给line2,这一点和其他类型不一样,它指定了一个指定长度的char型数组(这也是char型自身决定的),所以它被看做一个带长度的char*指针,不能赋字符串常量给它,只能将长度为5的char数组(也就是char[5])的地址给其赋值

6.取地址&,去地址*

#include
int main(void) { int a = 1; int *p = &(*((int*)&a)); printf("The value is: %d/n", *p); return 0; }/*The value is: 1 */

&还有一个作用是取地址,上面提到过取一个变量的地址。而*呢?对应的是去往那个地址所在的地方,将值给拿出来,将其称为去地址。

上面这个等式中就用到了这个两者:先将&a地址值转换成一个int *指针,再对这个指针去地址,那就是去变量a的地址上的值了呗,之后再将这个值的地址(还是a的地址)进行一个取地址,赋给指针变量(指针不就是专门用来存地址值的呗)

上面两个操作符号,正式一点叫&引用,和解除引用(去引用),&(((int*)&a)); 中第一个&的作用也叫脱去解引用,顾名思义:脱掉这个解引用(感觉绕来绕去还不如取地址和去地址简单易懂….@__@),具体的做法草考给出的博文链接

总结:

大致先给出这些,以后还有补充继续写后篇

有什么错误还希望指出

参考文章链接:

你可能感兴趣的文章
js获取url参数值的两种方式详解
查看>>
java中System.getProperty()方法详解
查看>>
MyEclipse设置默认注释的格式
查看>>
同一服务器部署多个tomcat时的端口号修改详情
查看>>
常用正则表达式集锦
查看>>
Spring定时器的时间表达式
查看>>
fastdfs简介
查看>>
主键和唯一索引的区别
查看>>
linux下使用yum安装gcc详解
查看>>
aclocal安装依赖的库
查看>>
String和常量池值的变化
查看>>
FastDFS 安装及使用详解
查看>>
ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)解决方案
查看>>
Host 'XXX' is not allowed to connect to this MySQL server解决方案
查看>>
corosync pacemaker 配置高可用集群(一)
查看>>
5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
查看>>
nginx(一) nginx详解
查看>>
nginx(二) nginx编译安装 及 配置WEB服务
查看>>
nginx(三) nginx配置:反向代理 负载均衡 后端健康检查 缓存
查看>>
nginx(四) nginx+keepalived 实现主备+双主热备模型的高可用负载均衡代理服务
查看>>