一,指针与引用概念的不同
1,指针:它是存放地址的一个变量,在逻辑上它是独立的。指针是可以被改变的,包括其所包含的地址的改变和其指向的内存中保存的数据的改变。
2,引用:是地址的别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须一开始就被初始化,而且引用不能改变,自始至终只能依附于一个变量。
二,传指针与传引用在本质上的不同
1,传指针
传址的本质是传值,被调函数的形参作为被调函数的局部变量处理,形参保存实参传递进来的值。
2,传引用
被调函数的形参也作为函数的局部变量处理,此时实参传进来的是地址。
3,总结
使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参,而引用更容易使用,更清晰。
三,从编译的角度理解指针与引用的不同
程序在编译时分别将指针与引用添加到符号表上,在符号表上记录的是变量名 及变量对应的地址。指针变量在符号表上对应的地址值为指针变量的地址,而引用在符号表上对应的地址值为引用对象的地址。符号表在生成之后不会发生改变,因此,指针变量中保存的地址值可以改变,即可以指向另外一块内存地址,引用在符号表上对应的地址值为引用对象的地址,因此,引用在初始化之后就不能改变。
四,指针与引用的相同点与不同点
1,相同点:
两者都是地址的概念,指针变量拥有自己的内存,可以在指针变量中保存另外一块内存空间的地址;而引用是某块内存的别名。
2,不同点:
(1),指针是一个实体,引用是地址的别名。
(2),如果定义一个应用,在定义时一定要进行初始化,不然编译时会出错,而且引用在初始化之后不能改变;但是指针变量中保存的地址可以发生改变。
(3),引用不能为空,但是指针可以为空。
(4),使用"sizeof 引用"得到的是引用对象的大小;而使用"sizeof 指针"得到的是指针变量的大小。
(5),指针有指针常量与常量指针,引用只有常引用没有引用常量。
五,传指针引用
创建带头结点的单链表
#include <iostream>
using namespace std;
struct ListNode{
int data;
struct ListNode *next;
};
void createList(ListNode *&head, int nums[], int n){
ListNode *pre, *p;
head = new ListNode;
head->data = -1;
head->next = NULL;
pre = head;
for(int i = 0; i < n; i ++){
p = new ListNode;
p->data = nums[i];
p->next = NULL;
pre->next = p;
pre = p;
}
}
int main(){
ListNode *head = NULL;
int n = 5;
int nums[] = {1, 2, 3, 4, 5};
createList(head, nums, n);
return 0;
}
分析:
如果不传指针引用只传递指针,例如,createList函数定义如下:
void createList(ListNode *head, int nums[], int n)
在主调函数中传递实参head的值给形参,形参是实参的副本,在被调函数中修改形参的值,主调函数中的实参变量head的值不会发生变化。如果是传引用,形参是实参的别名,对形参的修改将影响到实参,这也是我们想看到的。
六,常指针与指针常量
指针(*)与常量(const)谁在前先读谁,例如:
int main(){
int a = 10;
int b = 20;
int const *p1 = &a; //const在前,定义为常指针
int * const p2 = &b; //*在前,定义为指针常量
return 0;
}
1,常指针
指针指向的是常量,不能通过指针改变指向的变量的值,但是指针保存的地址可以改变。
2,指针常量
指针是常量,不能改变指针保存的地址,但是可以改变指针指向的变量。