php 變量範圍分析,內存分配和global,static關鍵字對變量的影響

變量分析

  • 局部變量:在函數(或者類的方法)內部,對應的變量爲局部變量,僅應用於函數內部,生命週期:宏觀的說,與函數生命週期基本一致,函數調用結束,局部變量也就被銷燬了;
  • 全局變量:不在函數內部,一般指一個腳本中函數外部的變量,即全局變量,僅應用於當前頁面,包括include或require的所有文件,生命週期:宏觀的說,與腳本執行週期一致,腳本執行結束,全局變量也就被銷燬了;
  • 超全局變量:php預定義的幾個變量,其作用於在任何一個腳本的任何範圍,即應用於整個網站 。常見的如下:$GLOBALS 、$_SERVER、$_REQUEST、$_POST 、 $_GET 、 $_FILES 、$_ENV 、 $_COOKIE、$_SESSION。
    具體實例可以參考文章C語言全局變量和局部變量講解

但是這裏要提到的是,php的全局變量有別於C/C++的全局變量的使用,即在函數外定義好全局變量後,如果在函數內部使用,需要使用global關鍵字或者$GLOBALS,具體代碼如下:

$a=5;//定義全局變量
$b=6;
function test(){
	global $a,$b;   
    
    echo 'a='. ++$a;
    echo 'b='.$b;
		
}
test();
//printf a=6 b=6
$a=5;//定義全局變量
$b=6;
function test(){	 
    $c=$GLOBALS['a']+$GLOBALS['b'];
    echo $c;	
}
test()
//printf  11

注意 global關鍵字和$GLOBALS雖然效果一樣,但其實現機制是不同的,根據php官方文檔,描述如下:

在 Zend 引擎 1 代,它驅動了 PHP4,對於變量的 static 和 global 定義是以引用的方式實現的。例如,在一個函數域內部用 global 語句導入的一個真正的全局變量實際上是建立了一個到全局變量的引用。
1.$GLOBALS[‘var’]是外部的全局變量本身。
2.global $var是外部全局變量$var的同名引用。

內具體參考php文檔全局和靜態變量的引用

可以通過下面代碼體會他們的不同:

$a=5;//定義全局變量
$b=6;
function test(){
	global $a,$b;   
    
	unset($a);
    echo 'a='.$a;
    echo 'b='.$b;
		
}
test();
echo 'a1='.$a;
//printf  Notice: (Undefined variable: a in 。。。) a= b=6 a1=5

可以看到unset之後,函數外部的a照常輸出,僅函數內部的a被銷燬。

$a=5;//定義全局變量
$b=6;
function test(){	 
    $GLOBALS['z']=$GLOBALS['a']+$GLOBALS['b'];
    unset($GLOBALS['a']);  
    echo 		
}
test();
echo 'z='.$z;
echo 'a1='.$a;
//printf  z=11 (Notice: Undefined variable: a  in ...  Notice: Undefined variable: a1 in ...)  a =   a1= 

可以看到unset之後,函數外部、內部的a都被銷燬。

注: 本文的銷燬並不表示變量所佔的內存被釋放,具體可以看看php的內存管理機制。

靜態變量

靜態變量就是通過static關鍵字修飾的變量,可以分爲全局靜態變量和局部靜態變量,需要注意的是,靜態變量的生命週期:全局靜態變量與腳本執行週期一致,也就是與普通全局變量一致,而局部靜態變量就與普通局部變量不同了,它是隨着函數被調用開始而開始,腳本執行結束而結束。具體看以下代碼:

static $a = 1;
function test1(){
	static $a=5;
	echo ++$a;
}

function test2(){
    global $a;
    echo ++$a;
}
function test3(){
   $a = 100;
    echo ++$a;
}
test1();  //6
test2();  //2
test1();  //7
test3();  //101
test2();  //3
test3();  //101

執行上述代碼,可以有以下結論:

  1. 局部靜態變量與全局靜態變量不會衝突,當變量名字相同時。
  2. 靜態變量僅在調用時初始化一次。

變量存儲位置(內存分配)

就所有語言(C/C++/JAVA/PHP/Python)而言,程序運行時會被分配內存塊,而內存在邏輯上大致區分爲棧區、堆區、全局區(靜態區)、代碼區、常量區,但不同語言又會有所差別。php不同區的大致作用如下:

  1. 棧:存放變量名,對象名,(也會有一部分的整型,浮點型,布爾,數字型存放在此處),特點速度快,佔內存空間小,棧中變量的內存會隨着定義所在區間的結束自動釋放,以局部變量$var=1爲例,在函數開始調用時分配動態存儲空間,函數結束時釋放這些空間;
  2. 堆:c語言中由程序員調用malloc()函數來主動申請的,需使用free()函數來釋放內存,若申請了堆區內存,之後忘記釋放內存,很容易造成內存泄漏 。php中由new申請的內存塊(即對象)放在堆中,~~由程序員釋放(編譯器不管),一般一個new與一個delete對應,一個new[]與一個delete[]對應。如果程序員沒有釋放掉,資源將由操作系統在程序結束後自動回收 。~~還有一些存儲長度不一致, 佔用內存較大的數據也放在堆裏,特點速度慢,佔的空間大。
  3. 全局區:存放靜態變量,全局變量,其中的變量在程序運行期間會一直存在,不會釋放;
  4. 代碼區:存放要執行的函數和方法的opcode;
  5. 常量區:存放常量。常量區的規則只能寫一次,所以其中的常量在程序運行期間會一直存在,不會釋放,且常量在其中只有一份拷貝,不會出現相同的常量的不同拷貝。

區分變量與數據類型的關係。
運行該腳本

<?php
$var1='我是字符串,在堆裏面';
static $var2=12;
$var3=1;
echo $var3;
?>

內存分配大致如下圖所示:
在這裏插入圖片描述
如圖,通過變量名與變量值的映射,完成變量的內存管理。

對象的管理
針對一下代碼做測試,會發現對象有別於其他數據類型(數組,字符串,布爾等)。

對象:

class test{
	public $num;   
 }
 $s=new test;
$s->num=5;
echo $s->num;  //5

$b=$s;
$b->num=10;
echo $s->num;  //10
echo $b->num;  //10

$q=&$s;
$q->num=9;
echo $s->num; //9
echo $q->num; //9

$x=clone $s;
$x->num=100;
echo $s->num;  //5
echo $x->num;  //100

其他數據類型,以數組爲例:

$b=array(array('5'),array('10'));
$c=&$b;
$c=array(array('50'),array('100'));
var_dump($b);  //array(array('50'),array('100'))
var_dump($c); //array(array('50'),array('100'))

$d=$b;
$d=array(array('50'),array('100'));
var_dump($b); //array(array('5'),array('10'))
var_dump($d); //array(array('50'),array('100'))

可以看到,php對象的管理有別於其他數據類型,對象引用和間接賦值效果一樣,而其他數據類型則不同,具體可以看看php變量的引用賦值與傳值賦值的詳細介紹

參考博文

  1. java與php 的static區別
  2. php中static 靜態關鍵字在類中的用法
  3. C/C++中static關鍵字的用法
  4. c語言中static關鍵字用法詳解
  5. 通過案例講解PHP static 關鍵字作用
  6. C語言中局部變量與全局變量在內存中的存放位置
  7. 全局變量和局部變量在內存中的區別
  8. 什麼是代碼區、常量區、靜態區(全局區)、堆區、棧區?
  9. PHP,常量/變量與內存間的關係
  10. php對象的內存分配機制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章