php引用(&)詳解及注意事項#很是好的一篇文章 標記一下#


(接上一篇... )php

php引用(&)詳解及注意事項html

官方文檔:

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

 

 

 

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

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

1.變量的引用

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

<?
    
$a = " ABC " ;
    
$b   =& $a ;
    
echo   $a ; // 這裏輸出:ABC
     echo   $b ; // 這裏輸出:ABC
     $b = " EFG " ;
    
echo   $a ; // 這裏$a的值變爲EFG 因此輸出EFG
     echo   $b ; // 這裏輸出EFG
?>

 


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

 

傳址調用我就很少說了 下面直接給出代碼函數

<? 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);的話就會出錯,緣由本身去想。oop

 

注意:post

    上面的「 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%以上。

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

 

 

 

 

 

 

 

 

 

 

摘自:http://www.php.net/manual/zh/language.references.whatdo.php

 

 

 

引用作什麼

PHP 的引用容許用兩個變量來指向同一個內容。意思是,當這樣作時:

<?php
$a 
=& $b;
?>
這意味着 $a 和 $b 指向了同一個變量。

Note:

$a 和 $b 在這裏是徹底相同的,這並非 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一個地方。

Note:

若是具備引用的數組被拷貝,其值不會解除引用。對於數組傳值給函數也是如此。

Note:

若是對一個未定義的變量進行引用賦值、引用參數傳遞或引用返回,則會自動建立該變量。

Example #1 對未定義的變量使用引用

<?php
function foo(&$var) { }

foo($a); // $a is "created" and assigned to null

$b = array();
foo($b['b']);
var_dump(array_key_exists('b'$b)); // bool(true)

$c = new StdClass;
foo($c->d);
var_dump(property_exists($c'd')); // bool(true)
?>

一樣的語法能夠用在函數中,它返回引用,以及用在 new 運算符中(PHP 4.0.4 以及之後版本):

<?php
$bar 
=& new fooclass();
$foo =& find_var($bar);
?>
自 PHP 5 起, new 自動返回引用,所以在此使用  =& 已通過時了而且會產生 E_STRICT 級別的消息。

Note:

不用 & 運算符致使對象生成了一個拷貝。若是在類中用 $this,它將做用於該類當前的實例。沒有用 & 的賦值將拷貝這個實例(例如對象)而且 $this 將做用於這個拷貝上,這並不老是想要的結果。因爲性能和內存消耗的問題,一般只想工做在一個實例上面。

儘管能夠用 @ 運算符來抑制構造函數中的任何錯誤信息,例如用 @new,但用 &new 語句時這不起效果。這是 Zend 引擎的一個限制而且會致使一個解析錯誤。

Warning

若是在一個函數內部給一個聲明爲 global 的變量賦於一個引用,該引用只在函數內部可見。能夠經過使用 $GLOBALS 數組避免這一點。

Example #2 在函數內引用全局變量

<?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 只改變了本地變量的引用。

Note:

若是在 foreach 語句中給一個具備引用的變量賦值,被引用的對象也被改變。

Example #3 引用與 foreach 語句

<?php
$ref 
0;
$row =& $ref;
foreach (array(
123) as $row) {
    
// do something
}
echo 
$ref// 3 - last element of the iterated array
?>

引用作的第二件事是用引用傳遞變量。這是經過在函數內創建一個本地變量而且該變量在呼叫範圍內引用了同一個內容來實現的。例如:

<?php
function foo(&$var)
{
    
$var++;
}

$a=5;
foo($a);
?>
將使 $a 變成 6。這是由於在 foo 函數中變量 $var 指向了和 $a 指向的同一個內容。更多詳細解釋見 引用傳遞

引用作的第三件事是引用返回

 

 

 

 

 

 

 

 

 數組引用的一個bug(後來仔細推敲,其實不是bug)

摘自:http://www.php.net/manual/zh/language.references.whatdo.php

 

 

It appears that references can have side-effects.  Below are two examples.  Both are simply copying one array to another.  In the second example, a reference is made to a value in the first array before the copy.  In the first example the value at index 0 points to two separate memory locations. In the second example, the value at index 0 points to the same memory location. 

I won't say this is a bug, because I don't know what the designed behavior of PHP is, but I don't think ANY developers would expect this behavior, so look out.

An example of where this could cause problems is if you do an array copy in a script and expect on type of behavior, but then later add a reference to a value in the array earlier in the script, and then find that the array copy behavior has unexpectedly changed.


<? php
//  Example one
$arr1   =   array ( 1 );
echo   " \nbefore:\n " ;
echo   " \$arr1[0] == { $arr1 [0]}\n " ;
$arr2   =   $arr1 ;
$arr2 [ 0 ] ++ ;
echo   " \nafter:\n " ;
echo   " \$arr1[0] == { $arr1 [0]}\n " ;
echo   " \$arr2[0] == { $arr2 [0]}\n " ;

輸出:

 

before:
$arr1[0] == 1

after:
$arr1[0] == 1
$arr2[0] == 2
 

 

 

 

// Example two

$arr3=array(1);
$a=&$arr3[0];
echo"\nbefore:\n";
echo"\$a == $a\n";
echo"\$arr3[0] == {$arr3[0]}\n";
$arr4=$arr3;
$arr4[0]++;
echo"\nafter:\n";
echo"\$a == $a\n";
echo"\$arr3[0] == {$arr3[0]}\n";
echo"\$arr4[0] == {$arr4[0]}\n";

 

輸出: 

 

 

before:
$a == 1
$arr3[0] == 1

after:
$a == 2
$arr3[0] == 2
$arr4[0] == 2

 

 

?>

 

 

 

分析說明:

    對於「Example two」,剛開始還覺得是個bug,其實仔細推敲,非也,分析以下,

在賦值(拷貝) 

$arr4 = $arr3 ;

 

以前,還有個對$arr3的第一個元素創建引用的過程,即

$a =& $arr3 [ 0 ];

 

因此在後來的賦值拷貝( $arr4=$arr3; ),會把這個引用一併拷貝過去,因此說

$a、$arr3[0]、$arr4[0] 三者實際上是引用關係,指向同一個地方。 

相關文章
相關標籤/搜索