首先概念上讲,指针从本质上说是存放变量地址的一个变量,在逻辑上是独立的,可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。
而引用是一个别名,他在逻辑上不是独立的,他的存在具有依附性,所以引用必须在一开始就被初始化,而且引用的对象在整个生命周期内是不能被改变的
(自始至终只能附于同一个变量)
在c++中指针和引用经常用于函数的参数传递然而 指针 传递参数和引用传递参数本质上是不同的
指针传递参数本质上是值传递的方式,他所传递的是一个地址值 值传递过程中 被调函数的形式参数作为被调函数的局部变量处理 即在栈中开辟了内存空间以存放由于主调函数放进来的实参的值 从而成为了实参的一个副本 值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行 不会影响主调函数的实参变量的值 (这里主要说的是实
参指针本身的地址值不会变)
指针和引用的相同点和不同点
相同点:都是地址的概念
指针指向一块内存 它的内容是所指内存的地址,而引用则是某块内存的别名
不同点:
指针是一个实体 而引用仅是个别名
引用只能在定义时被初始化一次 之后不可变;指针可变,引用“从一而终” 指针可以“见异思迁
引用没有const 指针有const const的指针不可变
引用不能为空 指针可以为空
Sizeof引用得到的是所指向的变量(对象)的大小 而“sizeof指针”得到的是指针本身的大小
指针和引用的自增运算意义不同
引用类型是安全的 而指针不是 引用比指针多了类型检查
例子如下: #include <iostream>
using namespace std;
void swap1(int a,int b);
void swap2(int *a,int *b);
void swap3(int &a,int &b);
int main(){
int a=2;
int b=3;
//swap1(a,b);
//swap2(&a,&b);
// swap3(a,b);
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
void swap1(int a,int b){//值传递
int temp;
temp=a;
a=b;
b=temp;
}
void swap2(int *a,int *b){//引用传递
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void swap3(int &a,int &b){
int temp=a;
a=b;
b=temp;
}
运行结果中 swap1:a=2 b=3
swap2:a=3,b=2
swap3:a=3,b=2
按照值传递
这种方式是继承自c语言 按值传递的特点是在函数调用时 系统会在函数作用域新建形参存储空间并用实参对其进行初始化 所以调用形参是实参的一个副本
Swap1中a是main中a的副本 b是main中b的副本 更改swap1函数的作用域中a和b的值并不影响main函数的值 swap2中传递的是main中a和b的地址 经过地址可以更改传
递过来的地址指向的值
按引用传递
这种传递是c++语言新添加的特性 通过传递参数的引用 可以允许函数访问甚至可以修改参数变量 要想按引用传递 必须将函数申明为引用
函数swap3达到了交换a和b的值的目的 从函数声明中可以看出swap3和swap1的唯一区别在于int后添加了一个引用符号 正是这个字符使得函数是swap3中的a和b是main函
数中a和b 的引用 对引用对象的任何操作 作用于原来的对象
对比两种传递方式
按值传递变量需要复制实参内存以初始化形参所以将会带来一些内存的开销
Java中的传递问题
JAVA 中变量有以下两种:
基本类型变量,包括boolean, byte, char,short,int ,long,float,double
引用类型变量,包括类,接口,数组(基本类型数组和对象数组)
对于基本类型和基本类型变量被当做参数传递给方法时,是值传递
在方法实体中,无法给原变量重新赋值,也无法改变他的值
而对象作为参数,如果在方法中把对象作为参数,方法调用时,参数传递的是对象的引用,即在方法调用时,实际参数把对象的引用传递给形式参数
这是实际参数与形式,参数指向同一个地址 即同一个对象,方法执行时,对形式参数的改变实际上就是对实际参数的改变,这个调用结束后就被保留下来。
值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参 数的值。
引用传递:也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际 参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。
下面举例说明:
传值---传递基本数据类型参数
public class PassValue{
static void exchange(int a, int b){//静态方法,交换a,b的值
int temp;
temp = a;
a = b;
b = temp;
}
public static void main(String[] args){
int i = 10;
int j = 100;
System.out.println("before call: " + "i=" + i + "\t" + "j = " + j);//调用前
exchange(i, j); //值传递,main方法只能调用静态方法
System.out.println("after call: " + "i=" + i + "\t" + "j = " + j);//调用后
}
}
运行结果:
before call: i = 10 j = 100
after call: i = 10 j = 100
说明:调用exchange(i, j)时,实际参数i,j分别把值传递给相应的形式参数a,b,在执行方法exchange()时,形式参数a,b的值的改变不影响实际参数i和j的值,i和j的值在调用前后并没改变。
引用传递---对象作为参数
如果在方法中把对象(或数组)作为参数,方法调用时,参数传递的是对象的引用(地址),即在方法调用时,实际参数把对对象的引用(地址)传递给形式参数。这是实际参数与形式参数指向同一个地址,即同一个对象(数组),方法执行时,对形式参数的改变实际上就是对实际参数的改变,这个结果在调用结束后被保留了下来。
class Book{
String name;
private folat price;
Book(String n, float ){ //构造方法
name = n;
price = p;
}
static void change(Book a_book, String n, float p){ //静态方法,对象作为参数
a_book.name = n;
a_book.price = p;
}
public void output(){ //实例方法,输出对象信息
System.out.println("name: " + name + "\t" + "price: " + price);
}
}
public class PassAddr{
public static void main(String [] args){
Book b = new Book("java2", 32.5f);
System.out.print("before call:\t"); //调用前
b.output();
b.change(b, "c++", 45.5f); //引用传递,传递对象b的引用,修改对象b的值
System.out.print("after call:\t"); //调用后
b.output();
}
}
运行结果:
before call: name:java2 price:32.5
after call: name:c++ price:45.5
说明:调用change(b,"c++",45.5f)时,对象b作为实际参数,把引用传递给相应的形式参数a_book,实际上a_book也指向同一个对象,即该对象有两个引用名:b和a_book。在执行方法change()时,对形式参数a_book操作就是对实际参数b的操作。