本文有不准确或不完善之处还望诸位小伙伴不吝赐教。
定义
在讲解这三者区别之前,让我们先明确一下这三者的具体确切的定义:
指针:在C语言之父Dennis MacAlistair Ritchie写的《C程序设计语言》种是这样定义指针的——指针是一种保存变量地址的变量。用图的形式来表述可能更清晰:如下图所示,c是一个char类型的变量,p是指向c的指针。图中可见,p中的内容即为c的地址。举个例子,双十一马上又到了,各位观音菩萨们免不了又要一通剁手,当我们下完单之后,快递小哥是怎么把我们的宝贝送到我们手上的呢?换句话说是怎么找到我们的?通过地址,也就是指针(温馨提示,购物虽好,可要保护好个人隐私哦~)。我们在下单之时就已经将自己的收货地址发给了店家,然后寄快递的时候就寄到这里,但是,实际上收快递的是谁呢?比如说我买东西填地址填的是北京市东城区景山前街4号,但是这个地址最终是为了找到我这个人,而不是说我填这个地址,快递给我扔到故宫就完事儿了。这样说能理解吗?
同样的道理,我们使用指针的目的也是如此,是为了操作最终指向的数据,这里也就是c。简单来说,指针就好像我们的家庭住址,你可以通过住址很轻松的找到一个人。
现在我们知道了,指针也是一个变量,而引入指针的目的是操作数据,那么如果用指针操作数据该怎么处理呢?这个问题先想一下,我们接着往下看。
‘&’:这是一个一元运算符,叫做取地址符。从名字能很容易地直到它是干嘛用的——取地址。当然这个符号还有很多其他用途,在这里我们只讨论取地址。
继续上面的图像中的例子,刚才我们讨论了p和c的关系,那么如果用‘&’应该怎么表示呢?如下:
p=&c;
等号左边含义为:获取变量c的存储地址(其实确切的说应该是存储变量c的存储单元的首位编号或编址)。
上式可以描述为,将c的地址赋值给变量p,我们称p为“指向”c的指针。
另外‘&’只能应用于内存中的对象,即变量与数组元素。不能作用于表达式、常量或register类型的变量。(register类型变量可以理解为寄存器变量,因为它是用在CPU寄存器中的,不一定每次都从内存中取,所以不能用取地址)
‘*’:你没有看错,就是
*.
我相信肯定有一部分同学觉得奇怪,刚才不是说过指针了吗?
注意了注意了,这里可不是指针,而是(让我们大声喊出它的名字)——一元运算符:间接寻址或间接引用运算符。当它作用于指针时将访问指针所指向的对象。
有没有发现一个很有意思的东西?
“当它作用于指针”?再回过头去看一下指针,发现没有?现在是不是更能理解为什么指针实际上是一个变量了?再想一下我们常见的指针出现的方式是怎样的呢?
*p,有没有?
还是上图中的例子,那么现在我们要用指针p访问变量c了,该怎么做呢?
*p=c;
到此为止,我们所要讨论的三个概念基本都已经明确了吧?
小结一下:
指针:一个变量,用来保存另外变量的地址。
‘&’:一元运算符——取地址符,用来获取变量存储地址。
‘*’:一元运算符——间接寻址或间接引用操作符,修饰指针用来访问指针所指向的对象。
若p为c的指针,则p=&c,*p=c.
下面附上资料下载链接
链接:https://pan.baidu.com/s/18gT9cNwJgxqhaZzqOvKQIA 提取码:4odr
数据结构中的应用
好了以上三者的概念我们已经解释的很详细了,接下来进入正题。标题已经说过,本文是对数据结构中的指针相关问题的解析。说了这么多最终必然还要回归数据结构(本文不详细展开数组中指针的使用)。
这里有个建议,大家在日常学习的时候,最好还是尽可能去搜索一些本源的知识,比如说我这边文章的所有理论,基本都诞生于C语言之父Dennis MacAlistair Ritchie写的《C程序设计语言(第2版)》,任何其他的书籍还是影像资料,都是加入了作者本人的观点的,我们既不能保证新加入的观点准确,也不能保证这种阐述更容易理解。所以还是尽可能从信息本源去获取,可以最大程度上降低信息传递误差。所以如果各位同学有余力的话,不妨在读过本文
并三连
之后,去看一下这本书中的相关表述。电子版下载链接我会放在本文中,请自行寻找。
言归正传,下面用一个例子讲一下数据结构中指针的使用。
仔细阅读下面这段代码:
#include <iostream>
using namespace std;
//#define MaxSize 50
typedef struct LNode{
int data;
LNode *next;
}LNode,*LList;
LList InitList(LList *L){//传入一个LList类型的指针(指向指针的指针),目的是改变指针L指向的内容
*L=(LList)malloc(sizeof(LNode));//*L取的是L指向的线性表头结点
if(*L==NULL)exit(0);
(*L)->next=NULL;//头结点的后继结点设为空
return *L;
}
LList CreateList(LList &L1){//传入一个LList类型的引用,取这个L结点的值,目的是改变指针L本身的内容————L=(LNode*)malloc(sizeof(LNode));
LNode *s;//声明一个结点
int x;
L1=(LList )malloc(sizeof(LNode));
L1->next=NULL;
scanf("%d",&x);
while(x!=9999){
s=(LList)malloc(sizeof(LNode));
s->data=x;
s->next=L1->next;
L1->next=s;
scanf("%d",&x);
}
return L1;
}
//上述两个方法的传参可以这样比较理解:第一个是传入指针的指针,目的是通过指针找到L(L本身也是一个链表指针)的内容,然后对这个指针的内容进行修改(或者说是地址是固定的,修改该地址上的内容);第二个是通过取地址符找到链表头结点的地址,然后对这个地址进行修改,让它变成另外一个地址,换句话说就是改变他的地址指向(新的地址上包含新定义的头结点)。
//总结一下就是,一个改内容地址不动,一个改地址内容也变。
int main()
{
LList L1;
CreateList(L1);
cout<<L1<<endl;//输出L1的值,因为L1实际是个链表指针,因此它的值就是一个哈希地址
cout<<&L1<<endl;//输出L1的地址,也是一个哈希地址,但是不同于上一个,这里指的是L1的地址,上一个指的是L1中存的地址———实际上L1链表头结点的地址
// cout<<*L1<<endl;
LList *L2;
InitList(L2);
cout<<&L2<<endl;
cout<<*L2<<endl;
cout<<L2<<endl;
return 0;
}
//再看下图这块儿入口函数,执行的时候会发现不报错但无法运行直接退出
/**
int main()
{
// LList L1;
//
// CreateList(L1);
// cout<<L1<<endl;
// cout<<&L1<<endl;
// cout<<*L1<<endl;
cout<<"========"<<endl;
LList *L2;
InitList(L2);
cout<<&L2<<endl;
cout<<*L2<<endl;
cout<<L2<<endl;
return 0;
}
*/
``
现在回过头来再去看你遇到的问题,里面的‘*’、‘&’都是干嘛的是不是一目了然了?