程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> PHP引用(&)使用詳解,php引用

PHP引用(&)使用詳解,php引用

編輯:關於PHP編程

PHP引用(&)使用詳解,php引用


初學php關於&引用

官方文檔:

1.引用是什麼:http://www.php.net/manual/zh/language.references.whatare.php

2.引用做什麼:http://www.php.net/manual/zh/language.references.whatdo.php
3.引用傳遞:http://www.php.net/manual/zh/language.references.pass.php
4.引用返回:http://www.php.net/manual/zh/language.references.return.php

 

php的引用(就是在變量或者函數、對象等前面加上&符號)

在PHP 中引用的意思是:不同的名字訪問同一個變量內容。
與C語言中的指針是有差別的.C語言中的指針裡面存儲的是變量的內容,在內存中存放的地址。

1.變量的引用

PHP 的引用允許你用兩個變量來指向同一個內容

復制代碼 <?
    $a="ABC";
    $b =&$a;
    echo $a;//這裡輸出:ABC
    echo $b;//這裡輸出:ABC
    $b="EFG";
    echo $a;//這裡$a的值變為EFG 所以輸出EFG
    echo $b;//這裡輸出EFG
?> 復制代碼

 


2.函數的引用傳遞(傳址調用

 

傳址調用我就不多說了 下面直接給出代碼

復制代碼 <?php
    function test(&$a)
    {
        $a=$a+100;
    }
    $b=1;
    echo $b;//輸出1
    test($b);   //這裡$b傳遞給函數的其實是$b的變量內容所處的內存地址,通過在函數裡改變$a的值 就可以改變$b的值了
    echo "<br>";
    echo $b;//輸出101
?> 復制代碼


要注意的是,在這裡test(1);的話就會出錯,原因自己去想。

 

注意:

    上面的“ test($b); ” 中的$b前面不要加 & 符號,但是在函數“call_user_func_array”中,若要引用傳參,就得需要 & 符號,如下代碼所示:

 

復制代碼 <?php

function a(&$b){
    $b++;
}
$c=0;

call_user_func_array('a',array(&$c));

echo $c;

//輸出 1

?> 復制代碼



3.函數的引用返回

 

先看代碼

復制代碼 <?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();得到的其實不是函數的引用返回,這跟普通的函數調用沒有區別 至於原因: 這是PHP的規定
PHP規定通過$a=&test(); 方式得到的才是函數的引用返回
至於什麼是引用返回呢(PHP手冊上說:引用返回用在當想用函數找到引用應該被綁定在哪一個變量上面時。) 這句狗屁話 害我半天沒看懂

用上面的例子來解釋就是
$a=test()方式調用函數,只是將函數的值賦給$a而已, 而$a做任何改變 都不會影響到函數中的$b
而通過$a=&test()方式調用函數呢, 他的作用是 將return $b中的 $b變量的內存地址與$a變量的內存地址 指向了同一個地方
即產生了相當於這樣的效果($a=&$b;) 所以改變$a的值 也同時改變了$b的值 所以在執行了
$a=&test();
$a=5;
以後,$b的值變為了5

這裡是為了讓大家理解函數的引用返回才使用靜態變量的,其實函數的引用返回多用在對象中

另附一個php官方例子: 

復制代碼 This is the way how we use pointer to access variable inside the class.

<?php
class talker{

    private $data = 'Hi';

    public function & get(){
        return $this->data;
    }
   
    public function out(){
        echo $this->data;
    }   

}

$aa = new talker();
$d = &$aa->get();

$aa->out();
$d = 'How';
$aa->out();
$d = 'Are';
$aa->out();
$d = 'You';
$aa->out();
?>

the output is "HiHowAreYou" 復制代碼

 

 

4.對象的引用

 

復制代碼 <?php
    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
?> 復制代碼

以上代碼是在PHP5中的運行效果

 

在PHP5中 對象的賦值是個引用的過程。上列中$b=new a; $c=$b; 其實等效於$b=new a; $c=&$b;
PHP5中默認就是通過引用來調用對象, 但有時你可能想建立一個對象的副本,並希望原來的對象的改變不影響到副本 . 為了這樣的目的,PHP5定義了一個特殊的方法,稱為__clone。

自 PHP 5 起,new 自動返回引用,因此在此使用 =& 已經過時了並且會產生 E_STRICT 級別的消息。

 

 

在php4中,對象的賦值是個拷貝過程,

如:$b=new a,其中new a產生的是一個匿名的a對象實例,而此時的$b是對這個匿名對象的拷貝。同理$c=$b,也是對$b內容的一個拷貝。所以在php4中,為了節省內存空間,$b=new a 一般會改成引用的模式,即 $b=& new a。

 

下面再來個 官方 提供的例子:

 在php5中,你不需要額外添加什麼東西就可到達“對象引用”的功能:

  

復制代碼 <?php
class foo{
        protected $name;
        function __construct($str){
                $this->name = $str;
        }
        function __toString(){
                return  'my name is "'. $this->name .'" and I live in "' . __CLASS__ . '".' . "\n";
        }
        function setName($str){
                $this->name = $str;
        }
}

class MasterOne{
        protected $foo;
        function __construct($f){
                $this->foo = $f;
        }
        function __toString(){
                return 'Master: ' . __CLASS__ . ' | foo: ' . $this->foo . "\n";
        }
        function setFooName($str){
                $this->foo->setName( $str );
        }
}

class MasterTwo{
        protected $foo;
        function __construct($f){
                $this->foo = $f;
        }
        function __toString(){
                return 'Master: ' . __CLASS__ . ' | foo: ' . $this->foo . "\n";
        }
        function setFooName($str){
                $this->foo->setName( $str );
        }
}

$bar = new foo('bar');

print("\n");
print("Only Created \$bar and printing \$bar\n");
print( $bar );

print("\n");
print("Now \$baz is referenced to \$bar and printing \$bar and \$baz\n");
$baz =& $bar;
print( $bar );

print("\n");
print("Now Creating MasterOne and Two and passing \$bar to both constructors\n");
$m1 = new MasterOne( $bar );
$m2 = new MasterTwo( $bar );
print( $m1 );
print( $m2 );

print("\n");
print("Now changing value of \$bar and printing \$bar and \$baz\n");
$bar->setName('baz');
print( $bar );
print( $baz );

print("\n");
print("Now printing again MasterOne and Two\n");
print( $m1 );
print( $m2 );

print("\n");
print("Now changing MasterTwo's foo name and printing again MasterOne and Two\n");
$m2->setFooName( 'MasterTwo\'s Foo' );
print( $m1 );
print( $m2 );

print("Also printing \$bar and \$baz\n");
print( $bar );
print( $baz );
?> 復制代碼

 

輸出:

復制代碼 Only Created $bar and printing $bar
my name is "bar" and I live in "foo".

Now $baz is referenced to $bar and printing $bar and $baz
my name is "bar" and I live in "foo".

Now Creating MasterOne and Two and passing $bar to both constructors
Master: MasterOne | foo: my name is "bar" and I live in "foo".

Master: MasterTwo | foo: my name is "bar" and I live in "foo".


Now changing value of $bar and printing $bar and $baz
my name is "baz" and I live in "foo".
my name is "baz" and I live in "foo".

Now printing again MasterOne and Two
Master: MasterOne | foo: my name is "baz" and I live in "foo".

Master: MasterTwo | foo: my name is "baz" and I live in "foo".


Now changing MasterTwo's foo name and printing again MasterOne and Two
Master: MasterOne | foo: my name is "MasterTwo's Foo" and I live in "foo".

Master: MasterTwo | foo: my name is "MasterTwo's Foo" and I live in "foo".

Also printing $bar and $baz
my name is "MasterTwo's Foo" and I live in "foo".
my name is "MasterTwo's Foo" and I live in "foo". 復制代碼

 

上個例子解析:
$bar = new foo('bar');
$m1 = new MasterOne( $bar );
$m2 = new MasterTwo( $bar );
實例對象$m1與$m2中的$bar是對實例$bar的引用,而非拷貝,這是php5中,對象引用的特點,也就是說
1.$m1或$m2內部,任何對$bar的操作都會影響外部對象實例$bar的相關值。
2.外部對象實例$bar的改變也會影響$m1和$m2內部的$bar的引用相關值。

 

在php4中,要實現如上述的 用一個對象實例去當著另外一個對象的屬性時,其等價代碼(即引用調用)類似如下:

class foo{
   var $bar;
   function setBar(&$newBar){
      $this->bar =& newBar;
   }
}

 

 

 

 

5.引用的作用
     如果程序比較大,引用同一個對象的變量比較多,並且希望用完該對象後手工清除它,個人建議用 "&" 方式,然後用$var=null的方式清除. 其它時候還是用php5的默認方式吧. 另外, php5中對於大數組的傳遞,建議用 "&" 方式, 畢竟節省內存空間使用。


6.取消引用
當你 unset 一個引用,只是斷開了變量名和變量內容之間的綁定。這並不意味著變量內容被銷毀了。例如: 

<?php
    $a = 1;
    $b =& $a;
    unset ($a);
?>  



不會 unset $b,只是 $a。 


7.global 引用
當用 global $var 聲明一個變量時實際上建立了一個到全局變量的引用。也就是說和這樣做是相同的: 

<?php
    $var =& $GLOBALS["var"];
?>  


這意味著,例如,unset $var 不會 unset 全局變量。 

 

 

如果在一個函數內部給一個聲明為 global 的變量賦於一個引用,該引用只在函數內部可見。可以通過使用 $GLOBALS 數組避免這一點。

Example  在函數內引用全局變量

復制代碼 <?php
$var1 = "Example variable";
$var2 = "";

function global_references($use_globals)
{
    global $var1, $var2;
    if (!$use_globals) {
        $var2 =& $var1; // visible only inside the function
    } else {
        $GLOBALS["var2"] =& $var1; // visible also in global context
    }
}

global_references(false);
echo "var2 is set to '$var2'\n"; // var2 is set to ''
global_references(true);
echo "var2 is set to '$var2'\n"; // var2 is set to 'Example variable'
?> 復制代碼 把 global $var; 當成是 $var =& $GLOBALS['var']; 的簡寫。從而將其它引用賦給 $var 只改變了本地變量的引用。

 

 

 

 

8.$this
在一個對象的方法中,$this 永遠是調用它的對象的引用。


//下面再來個小插曲
php中對於地址的指向(類似指針)功能不是由用戶自己來實現的,是由Zend核心實現的,php中引用采用的是“寫時拷貝”的原理,就是除非發生寫操作,指向同一個地址的變量或者對象是不會被拷貝的。

通俗的講
1:如果有下面的代碼

<?
    $a="ABC";
    $b=&$a;
?>


其實此時 $a與$b都是指向同一內存地址 而並不是$a與$b占用不同的內存

2:如果在上面的代碼基礎上再加上如下代碼

<?php
  $a="EFG";
?>


由於$a與$b所指向的內存的數據要重新寫一次了,此時Zend核心會自動判斷 自動為$b生產一個$a的數據拷貝,重新申請一塊內存進行存儲

php的引用(就是在變量或者函數、對象等前面加上&符號)是個高級話題,新手多注意,正確的理解php的引用很重要,對性能有較大影響,而且理解錯誤可能導致程序錯誤!

很 多人誤解php中的引用跟C當中的指針一樣,事實上並非如此,而且很大差別。C語言中的指針除了在數組傳遞過程中不用顯式申明外,其他都需要使用*進行定 義,而php中對於地址的指向(類似指針)功能不是由用戶自己來實現的,是由Zend核心實現的,php中引用采用的是“寫時拷貝”的原理,就是除非發生 寫操作,指向同一個地址的變量或者對象是不會被拷貝的,比如下面的代碼:

 

$a = array('a','c'...'n');
$b = $a;

如 果程序僅執行到這裡,$a和$b是相同的,但是並沒有像C那樣,$a和$b占用不同的內存空間,而是指向了同一塊內存,這就是php和c的差別,並不需要 寫成$b=&$a才表示$b指向$a的內存,zend就已經幫你實現了引用,並且zend會非常智能的幫你去判斷什麼時候該這樣處理,什麼時候不 該這樣處理。

如果在後面繼續寫如下代碼,增加一個函數,通過引用的方式傳遞參數,並打印輸出數組大小。

 

function printArray(&$arr) //引用傳遞
{
   print(count($arr));

}
  printArray($a);

上面的代碼中,我們通過引用把$a數組傳入printArray()函數,zend引擎會認為printArray()可能會導致對$a的改變,此時就會自動為$b生產一個$a的數據拷貝,重新申請一塊內存進行存儲。這就是前面提到的“寫時拷貝”概念。

如果我們把上面的代碼改成下面這樣:

 

function printArray($arr)   //值傳遞
{
   print(count($arr));
}
   printArray($a);

上面的代碼直接傳遞$a值到printArray()中,此時並不存在引用傳遞,所以沒有出現寫時拷貝。

大家可以測試一下上面兩行代碼的執行效率,比如外面加入一個循環1000次,看看運行的耗時,結果會讓你知道不正確使用引用會導致性能下降30%以上。

自我理解:按傳值的話是與函數內的參數無關,相當於局部變量的作用,而按傳址(引用)的話卻與函數內的參數有關,相當於全局變量的作用.而從性能方面來說,看上面分析就夠..

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved