天天看点

指针与引用, 值传递与地址传递的关系

前言

“引用”的这个词我到经常使用到,但是它经常是作为动词出现的,而作为名词,我却很少用到。这个上次面试就问我指针和引用的区别,我就有点蒙圈了,因为对“引用”这个词确实不了解,然后面试官又问我知不知道值传递和地址传递,我差点拍案而起,心里想问我这个问题,是不是有点太简单了,然后回答的是:“值传递就是把变量的值直接传递给函数,在函数中可以使用但是无法修改原来的实参的值,而地址传递则是将变量的地址传递给函数形参,在函数中可以通过形参指针直接访问到变量所在的内存从而可以修改实参的值”。 后来面试官也没说什么。。。我一直认为“值传递”传递的变量的值,“地址传递”传递的变量的地址。 多好的逻辑,还很合理、顺口。 但是对于“引用”这个概念确实不了解,然后就是网上查资料,一查吓一跳啊,发现自己真的理解错了,特写一下笔记纪念下。

函数传递参数的两种方式: 值传递和地址传递

1. 引用的定义

首先,引用是C++语言对C语言的一个重要补充,而C语言中是不包含“引用”的。

我们可以对一个数据建立一个“引用”; 它的作用是为变量起一个别名。

例如,有个变量a,我们通过引用给a起一个别名:

int a = 10;

int &b = a;

则b可以成为变量a的一个引用。

此时:b = 10;

&b = &a; //变量a与他的引用的地址一样

而使用指针的话:

int *p = &a; //&:取地址

此时 *p = 10; // 因为p存储的是a的地址,所以p指向a,

2. 引用的注意事项:

1) 引用不是一种独立的数据类型,对引用只能声明,不能定义。 只能先定义一个变量,然后在声明对该变量的一个引用(起个别名)。

2) 声明一个引用时,必须同时对他进行初始化,即表明它代表那个变量。

3) 在声明一个引用后,不能再是指作为另一个变量的引用。只能初始化一次。

4) 不能建立数组的引用,包括建立对数组元素的引用。

int a[10];

int &p = a; //数组名也是个地址常量,

int &q = a[0]; //这些都是不合法的

5) 不能建立引用的引用, 也没有引用的指针。

int a = 10;

int &b = a; // 由此可以看出引用与原变量是不等价的。虽然地址一样,值一样。

int &c = b;

int p = b;

//这些都是不合法

6) 可以取引用的地址。 注意此时引用的地址等于变量的地址

int a = 10;

int &b = a; // &b == &a

int p = &b; // 等同于p = &a;

3. 引用的本质:

引用本质上就是一个指针型常量,在声明的时候为引用开辟了内存,但内存里存储的是变量的地址,这个指针是一样的。 但是有个特性是:引用的地址=变量的地址。 凡是可以使用引用的地址都可以使用指针来代替。C++引入引用的目的是为了简化对(指针)地址的操作,方便使用。

疑问: 为什么给引用分配了内存,在取引用的地址是依然是变量的地址???

4. 值传递和地址传递:

1)C语言中可以说只有值传递。

func1(int a, int b);
	  func2(int *a, int *b);
  // 实际调用过程中的形式:
  {
	  int a, b;
	  ... ...
	  func1(a, b);
	  func2(&a, &b);
  }
           

这两个都属于值传递,只是传递的值不同而已:

第一个传递的是变量的值;

第二个传递的是变量的地址。

传递变量的值:无论函数中如何操作形参,都不会影响到实参变量的值;

传递变量的地址:无论函数中如何操作形参,都不会影响到实参变量的地址; 不会改变实参地址;不会改变实参地址;但是可以改变这个地址里的值,也就是可以改变变量的值。但是我们传递的是变量的地址,从这个角度来看是和普通的值传递完全一致的。

2)C++中引入了地址传递:

func3(int &a, int &b);
  //实际调用中的形式:
  {
      int a ,b ;
      ... ...
      func3(a ,b);
  }
           

这才是严格意义上的地址传递,调用函数时使用的是变量,但是传递的却是变量的地址,传递的却是是变量的地址,传递的却是变量的地址。

简单的理解就是:

调用的函数参数完全一致 func(a ,b); 而实际上:

函数本身传递变量的值的话就是值传递;

函数本身传递的是变量的地址的话就是地址传递;

func1(a,b); func2(&a,&b); 这两个我们在传递的时候传递的我们指定的值,只不过值的类型不一致;

func3(a,b); 我们虽然传递的是变量,而函数实际传递的是他们的地址。这才是地址传递。就是说地址传递是函数自动传递的,而不是我们通过取地址等运算把真正的地址传递过去。虽然指针完全可以胜任相应的工作。

5. 引用的实例

/*
*  使用的是g++编译器,虽然很像C
*      只在于 C++兼容C语法
*/

#include<stdio.h>
void func(int &b)
{
    printf("\tb = %d , &b = %p\n",b, &b);

}

int main(int argc, char **argv)
{
   int a = 1;
   int &b = a;
   printf("\ta = %d , b = %d\n",a,b);
   b = 2;
   printf("\t&a = %p \n\t&b = %p\n", &a,&b);
   a++;
   func(a);
   b++;
   func(b);
   return 1;
}
           

结果为:

root# g++ pointer.c
root# ./a.out
	a = 1 , b = 1
	&a = 0x7fff8456775c 
	&b = 0x7fff8456775c
	b = 3 , &b = 0x7fff8456775c
	b = 4 , &b = 0x7fff8456775c

           

可以看出:变量a, 变量a的引用b, func的形参等都具有相同的地址,他们任意一个值的改变都可以影响到实参变量a的值。