四时宝库

程序员的知识宝库

PHP 代码中变量前“&”的深度解析 #php #程序

在 PHP 中,当变量前面出现 “&” 符号时,它表示引用,意味着不同的名字可以访问同一个变量内容。这与 C 语言的指针不同,“&” 在 PHP 中是符号表别名。

例如,有以下代码:

$a = "ABC";

$b = &$a;

echo $a; //这里输出:ABC

echo $b; //这里输出:ABC

$b = "EFG";

echo $a; //这里$a 的值变为 EFG 所以输出 EFG

echo $b; //这里输出 EFG

从这个例子可以看出,一旦变量被引用,对引用变量的操作会直接影响到被引用的变量。

在 PHP 中,变量名和变量内容是不一样的,因此同样的内容可以有不同的名字。最接近的比喻是 Unix 的文件名和文件本身,变量名是目录条目,而变量内容则是文件本身。引用可以被看作是 Unix 文件系统中的紧密连接。

与 C 语言的指针相比,PHP 的引用不是指针。比如在 C 语言中,释放一个指向变量的指针可能会导致变量不可访问或出现错误,但在 PHP 中,即使取消引用,只是断开了变量名和变量内容之间的绑定,并不意味着变量内容被销毁。例如:

$a = 1;

$b = &$a;

unset($a);

echo $b; //不会 unset $b,只是 $a,这里仍输出 1。

二、变量引用

(一)变量引用的示例

以下是一个更复杂的变量引用示例:

$data = [ 'a', 'b', 'c' ];

foreach ( $data as $key => $val ){

$val = & $data [ $key ];

}

var_dump( $data );

在这个例子中,通过循环将数组中的每个元素进行引用赋值。这样,当对其中一个元素进行修改时,其他引用该元素的变量也会相应地发生变化。比如,如果在循环后执行$data[0] = 'd';,那么所有引用这个位置的变量都会显示新的值'd'。

再看一个例子:

$arr = array('name'=>'baixiaoshi','age'=>23);

echo'<pre>';

print_r($arr);

echo'</pre>';

$arr2 = $arr;

$arr2['hobby'] = 'run';

echo'<pre>';

print_r($arr2);

echo'</pre>';

这里虽然一开始$arr2是对$arr的复制,但当修改$arr2时,实际上也影响了原始数组$arr的内容,这在一定程度上展示了变量引用的潜在影响,虽然这里不是直接使用 “&” 进行引用,但说明了变量之间的关联可能会导致意外的结果。

(二)取消引用的影响

当取消引用时,只是断开了变量名和变量内容之间的绑定,这意味着变量内容不会被销毁。例如:

$a = 1;

$b = &$a;

unset($b);

echo $a; //这里仍输出 1。

在这个例子中,取消引用$b后,$a的值并没有受到影响,只是$b不再与$a有任何关联。

再如:

$a = range(0,1000);

var_dump(memory_get_usage());

$b = & $a ;

var_dump(memory_get_usage());

unset($b);

var_dump(memory_get_usage());

在这个例子中,使用memory_get_usage()函数可以观察到内存的变化。当进行引用赋值后,内存的占用情况会有所变化。而当取消引用$b后,内存并没有立即释放,这进一步说明了取消引用只是断开了变量名和变量内容之间的绑定,而不是销毁变量内容。

三、函数引用

(一)函数的传址调用

在 PHP 中,函数可以通过引用传递变量。这意味着函数内对参数的修改会直接影响到外部的变量。例如:

function test(&$a){

$a=$a+100;

}

$b=1;

echo $b ;//输出1

test($b);

echo "<br>";

echo $b;//输出 101

在这个例子中,函数test接受一个引用参数$a,在函数内部对$a进行操作,将其值增加 100。由于参数是通过引用传递的,所以外部变量$b的值也被修改了。

需要注意的是,PHP 规定传递的引用不能为常量。如果尝试test(1);,会出现错误。

(二)函数的引用返回

在 PHP 中,特定的方式才能得到函数的引用返回。例如:

function &test(){

static $b=0;//申明一个静态变量

$b=$b+1;

echo $b ;

return $b ;

}

$a=test();//这条语句会输出$b 的值为 1

$a=5;

$a=test();//这条语句会输出$b 的值为 2

$a=&test();//这条语句会输出$b 的值为 3

$a=5;

$a=test();//这条语句会输出$b 的值为 6

解释其作用和原理:通过$a=&test();方式得到的才是函数的引用返回。用上面的例子来解释就是,$a=test()这种方式调用函数,只是将函数的值赋给$a而已,而$a做任何改变,都不会影响到函数中的$b。而通过$a=&test()方式调用函数呢,它的作用是将return $b中的$b变量的内存地址与$a变量的内存地址指向了同一个地方。即产生了相当于这样的效果($a=&$b;),所以改变$a的值也同时改变了$b的值。所以在执行了$a=&test();$a=5;以后,$b的值变为了 5。

引用返回用在当想用函数找到引用应该被绑定在哪一个变量上面时。当返回引用时,使用此语法:function &find_var ($param){/*...code... */return $found_var;}。这里必须在两个地方都用&符号,指出返回的是一个引用,也指出变量是作为引用的绑定。如果试图这样从函数返回引用:return ($this->value);,这将不会起作用,因为在试图返回一个表达式的结果而不是一个引用的变量。只能从函数返回引用变量。

四、对象引用

(一)对象引用的示例

在 PHP5 中,对象的复制是通过引用来实现的。例如:

class TvControl {}

class Tv {

private $color;

private $tvControl;

function __construct() {

$this->color = "black";

$this->tvControl = new TvControl();

}

function setColor($color) {

$this->color = $color;

}

function getColor() {

return $this->color;

}

function getTvControl() {

return $this->tvControl;

}

}

$tv1 = new Tv();

$tvControl1 = $tv1->getTvControl();

$tv2 = $tv1;

echo "引用类:";

var_dump($tv2);

在这个例子中, tv1 的引用,当修改 tv2 的对应属性也会发生变化。例如,如果执行 $tv1->setColor("white");,那么 $tv2->getColor() 也会返回 "white"。

再看另一个例子:

class a {

var $abc = "ABC";

}

$b = new a;

$c = $b;

echo $b->abc; //这里输出 ABC

echo $c->abc; //这里输出 ABC

$b->abc = "DEF";

echo $c->abc; //这里输出 DEF

这里同样展示了对象引用的效果, c 指向同一个对象,当 c 的对应属性也随之改变。

(二)特殊方法__clone

PHP 为了实现建立一个对象的副本,且希望原来的对象的改变不影响到副本,定义了一个特殊的方法称为 __clone。

例如:

class Cat {

public $name;

function __construct($name) {

echo 'Cat类启动';

$this->name = $name;

}

function __destruct() {

echo 'Cat类结束';

}

}

$a = new Cat("默默");

$b = clone $a;

echo "改变之前:<br>";

echo "a->name:". $a->name. "<br>";

echo "b->name:". $b->name. "<br>";

$a->name = "琳琳";

echo "改变之后:<br>";

echo "a->name:". $a->name. "<br>";

echo "b->name:". $b->name. "<br>";

在这个例子中,使用 clone 关键字创建了对象的副本 a 的属性时,副本 $b 的属性不会发生变化。

当使用 clone 关键字时,如果被 “克隆” 的类属性中的引用,则该引用被保留了,也就是说,副本中的引用与原类中的引用都指向同样的内存。为了解决这个问题,可以在类中定义 __clone 方法来调整对象的克隆行为。例如:

class Corporate_Drone {

// 属性和方法定义

function __clone() {

// 在这里可以对克隆的对象进行特定的处理

}

}

$original = new Corporate_Drone();

$cloned = clone $original;

通过在 __clone 方法中添加特定的逻辑,可以实现更灵活的对象克隆。例如,可以在克隆时对某些属性进行初始化或者修改,以满足特定的需求。

五、引用的作用及其他情况

(一)引用在程序中的作用

在程序较大的情况下,如果引用同一个对象的变量比较多,使用引用可以更方便地管理和操作这些变量。因为通过引用,可以直接对同一个对象进行操作,而不需要通过复制对象来传递数据,这样可以节省内存空间和提高程序的执行效率。例如,在处理大型数据结构时,如大数组,如果使用引用传递,可以避免不必要的内存复制,从而提高程序的性能。建议在这种情况下使用 “&” 方式进行引用,并且在使用完对象后,可以通过将引用变量设置为null的方式来手工清除对象,以释放内存资源。

(二)global 引用

在 PHP 中,当用global $var声明一个变量时,实际上建立了一个到全局变量的引用。这意味着它等价于$var = &$GLOBALS["var"];。例如:

$var1 = 1;

$var2 = 2;

function test_global_ref() {

global $var1,$var2;

$var1 = &$var2;

$var1 = 7;

}

test_global_ref();

echo $var1;

echo $var2;

在这个例子中,函数内对全局变量的引用操作导致了变量值的变化。使用global声明变量建立的引用,使得在函数内部可以直接操作全局变量,而不是创建一个局部副本。但是需要注意的是,使用global可能会导致代码的可读性和可维护性降低,因为全局变量的作用域较大,容易引起命名冲突和意外的变量修改。

(三)“this” 引用

在 PHP 的对象方法中,“this” 永远是调用它的对象的引用。例如:

class Person {

public $name;

public function setName($name) {

$this->name = $name;

}

public function getName() {

return $this->name;

}

}

$person = new Person();

$person->setName('John');

echo $person->getName(); //输出:John

在这个例子中,在setName方法中,使用$this->name来访问当前对象的$name属性,并将传递给该方法的参数分配给该属性。在getName方法中,同样使用$this->name来访问$name属性,并返回该属性的值。“this” 关键字允许在对象的方法中访问当前对象的属性和方法,是 PHP 面向对象编程中非常重要的一部分。

(四)“写时拷贝” 原理

PHP 引用采用的是 “写时拷贝” 的原理,就是除非发生写操作,指向同一个地址的变量或者对象是不会被拷贝的。例如:

$a = "ABC";

$b = $a;

//此时$a与$b都是指向同一内存地址而并不是$a与$b占用不同的内存

$a = "EFG";

//由于$a与$b所指向的内存的数据要重新写一次了,此时 Zend 核心会自动判断自动为$b生产一个$a的数据拷贝,重新申请一块内存进行存储。

这种机制可以在一定程度上节省内存,只有在真正需要修改数据时才进行拷贝操作,提高了程序的性能。同时,也需要注意这种机制可能会导致一些意外的结果,特别是在处理复杂的数据结构和变量引用时,需要仔细考虑变量的修改对其他引用变量的影响。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接